Merge lp:~joergberroth/ubuntu-system-settings/wifi-802-1x-configurations into lp:ubuntu-system-settings
- wifi-802-1x-configurations
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Jonas G. Drange |
Approved revision: | 1415 |
Merged at revision: | 1457 |
Proposed branch: | lp:~joergberroth/ubuntu-system-settings/wifi-802-1x-configurations |
Merge into: | lp:ubuntu-system-settings |
Diff against target: |
3222 lines (+2597/-142) 13 files modified
plugins/wifi/CMakeLists.txt (+4/-0) plugins/wifi/CertDialog.qml (+94/-0) plugins/wifi/CertPicker.qml (+58/-0) plugins/wifi/OtherNetwork.qml (+691/-43) plugins/wifi/certhandler.cpp (+348/-0) plugins/wifi/certhandler.h (+98/-0) plugins/wifi/plugin.cpp (+5/-0) plugins/wifi/wifidbushelper.cpp (+116/-16) plugins/wifi/wifidbushelper.h (+1/-1) tests/autopilot/ubuntu_system_settings/__init__.py (+125/-45) tests/autopilot/ubuntu_system_settings/tests/__init__.py (+18/-9) tests/autopilot/ubuntu_system_settings/tests/networkmanager.py (+960/-0) tests/autopilot/ubuntu_system_settings/tests/test_wifi.py (+79/-28) |
To merge this branch: | bzr merge lp:~joergberroth/ubuntu-system-settings/wifi-802-1x-configurations |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Jonas G. Drange (community) | Approve | ||
Matthew Paul Thomas | design | Pending | |
Review via email: mp+261920@code.launchpad.net |
This proposal supersedes a proposal from 2015-06-03.
Commit message
Added support for 802-1x wireless network configurations.
Description of the change
* missed an additional NUL-Termination for passing cert and key path. Prevented to successfully put them into nm-config using path-scheme.
Fine now.
Additionally:
As not all eap networks have cert files issued, one will be able to connect even without selecting one.
Maybe a security warning should be thrown in the dialog informing the user - if he sets certs option to None - that this is a lag in security (mitm attacks)
Jonas G. Drange (jonas-drange) wrote : Posted in a previous version of this proposal | # |
JkB (joergberroth) wrote : Posted in a previous version of this proposal | # |
> This is really great. Thanks for proposing this.
>
> I've added some comments, but they're mostly about your decision to make the
> Dialog an ItemPage. If this is not crucial to the implementation of support
> for 802-1x, we need to revert it.
>
> Also, if cert picking is not implemented, please remove all components that
> are unused. Please leave the UI bits in, but hide it with "visible:
> showAllUI". This way we can get strings translated.
>
> Thanks again.
Hey, thanks a lot for for reviewing.
First I had the concerns about pushing it to an ItemPage, too. Then, after dealing with the variety of different configurations, I found there would be an advantage if one changes to ItemPage. There is no need for the ItemPage from implementation point of view but there are configurations for which the user has to be asked up to two different certificates and an additional private key (TLS) and there can be different types of path2 authentication types for the different WPA Enterprise configurations.
So if we want the user to be able two choose all the different types of configurations, it seems to me that a dialog would get quite overloaded with ui inputs. And correct me if I'm wrong but scrolling, which certainly would be needed, seems not to be provided for the dialogs.
At the moment there are some pages in the cellular settings (e.g. APN) where one switched to ItemPages, too. So from design point of view it would keep consistent.
Maybe, based on these points, we can quickly discuss this again before I revert the change.
All other comments are clear. I will consider for the next commit.
I think the file picker would make selecting certificates even more comfortable but I will remove it as proposed, so one can work on that later.
Thanks again.
Joerg
Matthew Paul Thomas (mpt) wrote : Posted in a previous version of this proposal | # |
Thanks so much for working on this, Joerg!
Jonas is right, though, that the authentication UI does need to remain a dialog. The reason is that System Settings is just one of three -- and will eventually be just one of six -- different places that might launch the UI for entering Wi-Fi details. The first-run setup "Connect to Wi-Fi" screen, and the network indicator menu, should let you connect to all the same Wi-Fi networks that System Settings does, by putting up the same dialog. <https:/
When dialog contents are bigger than the available screen space, the body is supposed to scroll automatically, and apparently this has been implemented already (bug 1376763). If it's not working, please report a bug.
JkB (joergberroth) wrote : Posted in a previous version of this proposal | # |
Thanks for the review and the hints.
It was not clear from documentation for me. But the bug post made it.
I just reverted the page back to dialog.
Am 2015-04-27 um 13:04 schrieb Matthew Paul Thomas:
> Review: Needs Fixing design
>
> Thanks so much for working on this, Joerg!
>
> Jonas is right, though, that the authentication UI does need to remain a dialog. The reason is that System Settings is just one of three -- and will eventually be just one of six -- different places that might launch the UI for entering Wi-Fi details. The first-run setup "Connect to Wi-Fi" screen, and the network indicator menu, should let you connect to all the same Wi-Fi networks that System Settings does, by putting up the same dialog. <https:/
>
> When dialog contents are bigger than the available screen space, the body is supposed to scroll automatically, and apparently this has been implemented already (bug 1376763). If it's not working, please report a bug.
>
Matthew Paul Thomas (mpt) wrote : Posted in a previous version of this proposal | # |
Thank you.
Marcus Tomlinson (marcustomlinson) wrote : Posted in a previous version of this proposal | # |
Your code isn't compiling. There are some weird "!==" occurrences in 2 of your .cpp files. Rather hard to test your changes work when the project doesn't compile no? :P
Jonas G. Drange (jonas-drange) wrote : Posted in a previous version of this proposal | # |
Getting [1] when trying to build. Could you fix those? Thanks.
Matthew Paul Thomas (mpt) wrote : Posted in a previous version of this proposal | # |
Yesterday, before I saw this new merge proposal, I was tackling the same question: "Where to store the cert files?" Also private key files and PAC files. On Ubuntu for PC, the current answer is in their own app, "Passwords and Keys". But on phones/tablets the overall system UI real estate is much smaller, so as with software updates and system version, this task fits best in System Settings.
This means the same conclusion (3) that you came to: storing the files in System Settings. "For this reason, System Settings should be set up as the default handler for certificates, private key files, and PAC files that you download or open, showing a confirmation dialog with info about the certificate/file and “Cancel” and “Save” buttons, and then lowering itself behind the previous app." <https:/
A later task will be to design+implement a way to remove previously-saved certificates/files inside System Settings.
Jonas G. Drange (jonas-drange) wrote : Posted in a previous version of this proposal | # |
Thanks, Joerg. It builds and it looks good! I have a couple of questions and comments:
1. If file picker fails, I prefer that it was reverted to "None". What do you think?
2. Could you add some instructions on how to test this?
3. I don't quite understand the diff L98-200, why is the indentation changed?
4. L425, L465, L506, L559: Please use visible: showAllUI for things not yet implemented.
5. L431, L440, L482, L673, L675, L678, L869, L877, L885, L893, L901, L909: Extra space
6. L759: If it's not needed, please just remove it. If it's something we might have to do, maybe make it a TODO?
This would be difficult to test using autopilot, but we should try. I can help you, if you can inform me on what the most common WPA2 Enterprise network looks like, and what the user needs to do, using your UI, to connect to it.
Thanks again.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:1401
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
JkB (joergberroth) wrote : Posted in a previous version of this proposal | # |
Jonas, thanks for reviewing.
First,
4.-6. To which diff/file do your line numbers refer to. I don't get them
aligned with my r1401 into r1435.
1. You are right. Also, if a cert is added, the added cert item should
be selected after closing the dialog. I will keep on working on that.
2. To fully test this, ideally, proper networks have to be available to
connect to. As they are not easy to set up or even seem to be hardware
dependent, one may think about if its possible to emulate the networks.
What I could test so far, besides the typical WPA and WEP, was a
TTLS/MSCHAP2 network. The TTLS is typically used for "eduroam",
available at most universities all over europe. They might differ in the
phase-2-
university.
If you just want to test the creation of the right configuration it
should be sufficient to check the configuration file for the network in
/userdata/
them to example files. These files are created even if no network is
available. There was already an error handling integrated that prints
wpa_supplicant errors in the feedback dialog. So e.g. if your cert file
does not contain the content it should, it will be wpa_supplicant that
reports this.
I don't know what wifi infrastructures you have around, but I believe it
is hard to get everyone.
I've heard of wpa_supplicant being able to emulate different WPA
networks, but I had not have the time to look into that.
My connection test on a TTLS/MSCHAPv2 eduroam was established
successfully. Maybe it would be best to let people test it with there
networks.
Hence, for testing and maybe autopilot testing for an example connection
- lets take the TTLS/MSCHAPv2 - one has to go through the following steps:
1. type in network name (SSID)
2. select WPA Enterprise
3. select auth TTLS
4. select inner auth MSCHAPv2
5. CA certificate ( lets state that no one is installed) -> select choose...
6. use content hub to import a cert file (.pem supported at the moment)
7. content hub will import it to the Documents folder of
ubuntu-
8. the file will be shown on the CertDialog and the user can decide to
save the file.
9. If that file is not empty, it will be moved to the wifi/ssl/certs/
folder. The data model will use the files in that folder to build the
itemselector.
10. Choose the right ca cert.
11. type in anonym identity, identity and Password.
12. then connect. If the network will not be found, an error message
will be thrown.
I do not know anything about autopilot testing so far. If you believe we
can get this running, I just could support you.
Thanks,
Joerg
Am 2015-06-05 um 14:11 schrieb Jonas G. Drange:
> Review: Needs Fixing
>
> Thanks, Joerg. It builds and it looks good! I have a couple of questions and comments:
>
> 1. If file picker fails, I prefer that it was reverted to "None". What do you think?
> 2. Could you add some instructions on how to test this?
> 3. I don't quite understand the diff L98-200, why is the indentation changed?
> 4. L425, L465, L506, L559: Please use visible: showAllUI for things not yet implemented.
> 5. L431, L440, L482, L673...
Jonas G. Drange (jonas-drange) wrote : Posted in a previous version of this proposal | # |
On 5 June 2015 at 20:57, JkB <email address hidden> wrote:
> Jonas, thanks for reviewing.
>
>
Happy to!
> First,
> 4.-6. To which diff/file do your line numbers refer to. I don't get them
> aligned with my r1401 into r1435.
>
>
I meant the web diff [1]. I am not certain as to why I did not use the
inline comment system. Sorry!
> 1. You are right. Also, if a cert is added, the added cert item should
> be selected after closing the dialog. I will keep on working on that.
>
>
Awesome.
> 2. To fully test this,
> …
> "eduroam"
> [is]
> available at most universities all over europe.
Me and my brother both have Ubuntu Phones and attend university, so we'll
give eduroam a go. Good idea!
> If you just want to test the creation of the right configuration it
> should be sufficient to check the configuration file for the network in
> /userdata/
> them to example files. These files are created even if no network is
> available.
Great. That means we can mock (imitate) NetworkManager and query it
directly over dbus.
> I don't know what wifi infrastructures you have around, but I believe it
> is hard to get everyone.
> I've heard of wpa_supplicant being able to emulate different WPA
> networks, but I had not have the time to look into that.
>
I don't think that's necessary at this stage. WPA2 enterprise isn't
critical judging by the level of bug reporting.
> My connection test on a TTLS/MSCHAPv2 eduroam was established
> successfully. Maybe it would be best to let people test it with there
> networks.
>
>
Agreed.
> Hence, for testing and maybe autopilot testing for an example connection
I can start this when I have free cycles.
> I do not know anything about autopilot testing so far. If you believe we
> can get this running, I just could support you.
>
>
No worries. I'll propose a branch against this branch.
Thanks,
Jonas
JkB (joergberroth) wrote : Posted in a previous version of this proposal | # |
Am 2015-06-05 um 22:51 schrieb Jonas G. Drange:
> On 5 June 2015 at 20:57, JkB <email address hidden> wrote:
>
>> First,
>> 4.-6. To which diff/file do your line numbers refer to. I don't get them
>> aligned with my r1401 into r1435.
>>
>>
> I meant the web diff [1]. I am not certain as to why I did not use the
> inline comment system. Sorry!
>
> [1]
> https:/
>
Checked that, but still don't get it, where is the offset?
>
>
>> If you just want to test the creation of the right configuration it
>> should be sufficient to check the configuration file for the network in
>> /userdata/
>> them to example files. These files are created even if no network is
>> available.
>
>
> Great. That means we can mock (imitate) NetworkManager and query it
> directly over dbus.
Yes, sounds like an idea!
>
>
Jonas G. Drange (jonas-drange) wrote : | # |
Great. Only two minor comments. :) I have yet to test this on eduroam, but will hopefully get it tested this week.
JkB (joergberroth) wrote : | # |
Fixed that and also improved getting secrets for PreviousNetworks page.
Looking forward to your test results.
Thanks again.
Am 2015-06-15 um 15:33 schrieb Jonas G. Drange:
> Review: Needs Information
>
> Great. Only two minor comments. :) I have yet to test this on eduroam, but will hopefully get it tested this week.
>
> Diff comments:
>
>> === modified file '.bzrignore'
>> --- .bzrignore 2014-07-25 08:22:52 +0000
>> +++ .bzrignore 2015-06-15 00:18:56 +0000
>> @@ -1,22 +1,2 @@
>> -/build
>> -*.debhelper
>> -*.log
>> -*.moc
>> -CMakeLists.
>> -moc_*
>> -Makefile*
>> -tst_*
>> -lib/SystemSett
>> -src/qrc_ui.cpp
>> -src/system-
>> -system-
>> -/debian/files
>> -/debian/
>> -/debian/
>> -/debian/tmp/
>> -/debian/
>> -/debian/
>> -.cproject
>> -.project
>> -.settings
>> -__pycache__
>> +*.txt_testingjkb
>
> What warrants all these changes to the bzrignore?
>
>> +./CMakeLists.
>>
>> === modified file 'plugins/
>> --- plugins/
>> +++ plugins/
>> @@ -1,6 +1,8 @@
>> set(QML_SOURCES
>> AccessPoint.qml
>> BaseMenuItem.qml
>> +CertPicker.qml
>> +CertDialog.qml
>> Common.qml
>> DivMenuItem.qml
>> FramedMenuItem.qml
>> @@ -23,10 +25,12 @@
>> plugin.cpp
>> unitymenumodels
>> previousnetwork
>> + certhandler.cpp
>> wifidbushelper.h
>> plugin.h
>> unitymenumodels
>> previousnetwork
>> + certhandler.h
>> nm_manager_proxy.h
>> nm_settings_proxy.h
>> nm_settings_
>>
>> === added file 'plugins/
>> --- plugins/
>> +++ plugins/
>> @@ -0,0 +1,92 @@
>> +import QtQuick 2.0
>> +import QtQuick.Layouts 1.1
>> +import Ubuntu.
>> +import Ubuntu.Components 0.1
>> +import Ubuntu.
>> +import Ubuntu.
>> +
>> +Component {
>> +
>> + Dialog {
>> + id: certDialog
>> +
>> + property var certType;
>> + property var fileName;
>> +
>> + signal updateSignal(var update);
>> +
>> + anchors.fill: parent
>> +
>> + title: {
>> + if (certType === 0){ // certificate
>> + {i18n.tr("Add certificate")+"?"};
>> + } else if (certType === 1){ // privatekey
>> + {i18n.tr("Add key")+"?"};
>> + } else if (certType === 2){ // pacFile
>> + {i18n.tr("Add pac file")+"?"};
>> + }
>> + }
>> +
>> + FileHandler {
>> + id: fileHandler
>> + }
>> +
>> + Label {
>> + id: certContentLabel
>> + text : {i18n.tr(
>> + objectName: "certContentLabel"
>> + fontSize: "medium"
>> + font.bold: false
>> + }
>> +
>> + TextArea {
>> + id : certContent
>> + objectName: "certContent"
>> + readOnly: true...
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1405
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Jonas G. Drange (jonas-drange) wrote : | # |
Tried connecting to an eduroam network yesterday. I don't have a syslog, and the System Settings log looked normal. However, this [1] is what System Settings produced in /etc/NetworkMan
Sadly the eduroam connection failed. I followed normal eduroam connection settings (PEAP, mschapv2).
Creating the same connection using nm-applet, creates the following /etc/NetworkMan
[1] http://
[2] https:/
[3] http://
JkB (joergberroth) wrote : | # |
Thanks for testing this with peap.
First, the type String is set by nm after sending over the configuration
data with the original "802-11-wireless" string as type.
So this seems to be changed by nm. I have not had a closer look t this,
yet. But I think this does not matter, as for my eduroam network
(TTLS/MChapv2) it works.
In general:
Have you got the orange feedback text over the button row? What does it
tell?
If nm fails with the configuration it states a warning visible in the
system-settings log in .cache/upstart/.
And anyway,
I haven't yet spent a lot time on the peap configuration,
and it seems that I forgot to add the ca-cert integration for PEAP!
Don't you need the ca-cert for your eduroam (wherever this is) ?
And than there are two more points:
the peap-label check and the version check.
At the moment I am not sure what is really needed to ask the user, so I
will check that as well
and come back with a fix tonight.
Jonas G. Drange (jonas-drange) wrote : | # |
On 25 June 2015 at 13:21, JkB <email address hidden> wrote:
> First, the type String is set by nm after sending over the configuration
> data with the original "802-11-wireless" string as type.
>
I'll confirm this using autopilot testing. But we need to find and
eliminate the case in which 'wifi' is sent by the binding (if ever). I'll
try to discover this via testing.
> as for my eduroam network
>
> (TTLS/MChapv2) it works.
>
It would be interesting to see this configuration if it works. What you
could do is to pastebin this file:
/etc/NetworkMan
Remove all private information before pasting :)
In general:
> Have you got the orange feedback text over the button row? What does it
> tell?
>
I did notice the feedback, but I saw in my syslog that it wouldn't
associate.
If nm fails with the configuration it states a warning visible in the
> system-settings log in .cache/upstart/.
>
Okay, will look there.
> And anyway,
> I haven't yet spent a lot time on the peap configuration,
> and it seems that I forgot to add the ca-cert integration for PEAP!
>
Looking forward to that :)
> Don't you need the ca-cert for your eduroam (wherever this is) ?
>
No, not according to the instructions on my Uni's site:
https:/
> And than there are two more points:
> the peap-label check and the version check.
> At the moment I am not sure what is really needed to ask the user, so I
> will check that as well
> and come back with a fix tonight.
Awesome!
JkB (joergberroth) wrote : | # |
Am 2015-06-25 um 15:06 schrieb Jonas G. Drange:
> It would be interesting to see this configuration if it works. What you
> could do is to pastebin this file:
>
This is the eduroam config written on connection on ubuntu phone:
http://
Never recognized the change in labeling before, though.
>
>> Don't you need the ca-cert for your eduroam (wherever this is) ?
> No, not according to the instructions on my Uni's site:
> https:/
Also, I added a hint in the dialog, that connecting to a network with
choosing a cert is more secure.
>> And than there are two more points:
>> the peap-label check and the version check.
I commited a fix against that. It would be great if you could test this
again.
Could be that the fault was in forcing "phase1-peaplabel" = 1. Hopefully.
Thanks.
Jonas G. Drange (jonas-drange) wrote : | # |
On 26 June 2015 at 08:04, JkB <email address hidden> wrote:
> http://
>
If that works for you, let's assume that connection.type 'wifi' is valid,
even though it's not documented.
Also, I added a hint in the dialog, that connecting to a network with
> choosing a cert is more secure.
>
Okay, but we need to make sure design is involved, so if there's nothing in
the spec [1] about this, we have to create a bug against System Settings
which affects ubuntu-ux. What's the basis for this hint?
> >> And than there are two more points:
> >> the peap-label check and the version check.
>
> I commited a fix against that. It would be great if you could test this
> again.
Will do! I see that the nm-applet (Ubuntu Desktop's way of connecting to
hidden networks) does not force phase1-peaplabel=1, however, it does
include password-flags=1 instead of password-flags=2.
Here's [2] the work on autopilot tests for your branch. There are some
notable changes that affects your branch, not directly related to autopilot
tests:
- Auth methods and protocols are no longer translated (e.g. PEAP,
MSCHAPv2, and so on.)
- Your feedback change has been reverted for the following reasons:
- It's in the spec [3].
- The tests depend on the feedback existing in the text property of
the Dialog.
- The Dialog provides the 'text' property, so why would we not use it
for feedback important to the user? :)
- If visibility is a problem, we should to scroll to the top of the
dialog. This is the pattern in other dialogs as well.
- One fixme has been re-added, but I'll take care of it. :)
I need suggestions on what to test. What are other popular WPA2 enterprise
configurations? Do you have suggestions, Joerg?
Also, anything you'd like me to test?
Thanks,
Jonas.
[1]
https:/
[2]
https:/
[3]
https:/
JkB (joergberroth) wrote : | # |
Am 2015-06-26 um 13:51 schrieb Jonas G. Drange:
> On 26 June 2015 at 08:04, JkB <email address hidden> wrote:
> If that works for you, let's assume that connection.type 'wifi' is valid,
> even though it's not documented.
Yes. And you'll see also for [wifi-security] one changed that from
[802-11-
>
> Also, I added a hint in the dialog, that connecting to a network with
>> choosing a cert is more secure.
>>
>
> Okay, but we need to make sure design is involved, so if there's nothing in
> the spec [1] about this, we have to create a bug against System Settings
> which affects ubuntu-ux. What's the basis for this hint?
You're right. I will comment it until than.
The idea behind this is, that without certification, the user can not be
sure that he is connecting to a real e.g. "eduroam" which could be a
security problem.
On the other side it's not up to the user to fix that but to the admin
that owns the network.
So, difficult.
>
>>>> And than there are two more points:
>>>> the peap-label check and the version check.
>>
>> I commited a fix against that. It would be great if you could test this
>> again.
>
>
> Will do! I see that the nm-applet (Ubuntu Desktop's way of connecting to
> hidden networks) does not force phase1-peaplabel=1, however, it does
> include password-flags=1 instead of password-flags=2.
That's right.
I removed forcing peap-label and put in an itemselector for choosing
peap-version instead as this is more common.
About password-flags: [1]
password-flags=2 is issued if the password shall not be saved.
I left the flags out if pw is to be saved. defaults to 0.
>
>
> Here's [2] the work on autopilot tests for your branch. There are some
Will have a look at this later.
> notable changes that affects your branch, not directly related to autopilot
> tests:
>
> - Auth methods and protocols are no longer translated (e.g. PEAP,
> MSCHAPv2, and so on.)
ok!
> - Your feedback change has been reverted for the following reasons:
> - If visibility is a problem, we should to scroll to the top of the
> dialog. This is the pattern in other dialogs as well.
That was the point I was thinking about. It is more user friendly to
have it appear right in sight over the "command" buttons.
But, you are right, scrolling would do in a way, too.
And as the tests already depend on that I will change it back.
Maybe one can think about this later.
> - One fixme has been re-added, but I'll take care of it. :)
I removed it, as I already translate it in the certhandler.cpp !
so if you add the translation in this file, we should remove it in the
handler. What is more common?
>
> I need suggestions on what to test. What are other popular WPA2 enterprise
> configurations? Do you have suggestions, Joerg?
I'm not a pro in that but during the last days ;-) I read of peap and
TLS being the most common. So, the TLS config would be a point to start.
>
> Also, anything you'd like me to test?
And as I have no auto tests, checking whether all the parameters are
sent and put in right into configuration would be great. I then would go
through all the config files created.
Really looking forward to your te...
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1409
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Jonas G. Drange (jonas-drange) wrote : | # |
> >> I commited a fix against that. It would be great if you could test this
> >> again.
I have tested connecting to eduroam on both Mako (Nexus 4) and Meizu's phone—it works! :)
I feel confident we are ready to land this, however, I just proposed merging [1] into this branch. It fixes the last fixmes. :)
JkB (joergberroth) wrote : | # |
Am 2015-06-29 um 16:05 schrieb Jonas G. Drange:
>>>> I commited a fix against that. It would be great if you could test this
>>>> again.
>
> I have tested connecting to eduroam on both Mako (Nexus 4) and Meizu's phone—it works! :)
Great! Have you tested using certificates for your connection as well?
>
> I feel confident we are ready to land this, however, I just proposed merging [1] into this branch. It fixes the last fixmes. :)
>
Ok. I will have a look at them tonight; It seems that I then can remove
translations in the certhandler.cpp itself.
Do you think it is really necessary to translate names (CommonName,
Organization) ?
Thanks.
Matthew Paul Thomas (mpt) wrote : | # |
"The idea behind this is, that without certification, the user can not be sure that he is connecting to a real e.g. "eduroam" which could be a security problem. On the other side it's not up to the user to fix that but to the admin that owns the network. So, difficult."
There is precedent for this: I specced that the dialog should warn that WEP networks are inherently insecure.
You seem to be talking about a different case, though, where connecting to a network without a certificate leaves you vulnerable to connecting accidentally to a different network with the same name later. But it's not clear to me how that problem is any worse for authentication types that allow certificates than for authentication types that don't. That's bug 1258496, which is a problem for any authentication type. If there's something that TLS/PEAP etc can do specifically for that, please comment in that bug report; or if you're talking about something different, report a separate bug.
Jonas G. Drange (jonas-drange) wrote : | # |
On 29 June 2015 at 17:51, JkB <email address hidden> wrote:
> Great! Have you tested using certificates for your connection as well?
>
No, I don't have this opportunity.
> Ok. I will have a look at them tonight; It seems that I then can remove
> translations in the certhandler.cpp itself.
>
No, “None” and “Choose” are still translated.
> Do you think it is really necessary to translate names (CommonName,
> Organization) ?
>
When we do i18n.tr(
choose how this is translated, but Organization name just replaces %1 and
is not translated.
So, in Norwegian, if we elided words using “–bork”, a translator just
changes “%1…” into “%1–bork”.
Thank you.
JkB (joergberroth) wrote : | # |
> No, I don't have this opportunity.
According to a script, I found for your University?! there is a cert
chain in the script [1].
You could try this....
@mpt:
The problem I talked about is in a way about the man in the tent as
described in the bug.
To my understanding, the advantage of WPA Enterprise networks is that
you can be sure to connect to the right access point, if it carries a
valid certificate (or let's say it's way harder to build an "evil twin"
as you don't own the cert).
For example, the University of Bergen works with certificates [1], but
not as a must have [2]. There seems to be an older auth scheme as well.
Maybe
@Jonas can clarify this?
In general this is a IT department thing and the user only could ask for
it. Thats's why I also question providing a hint.
Nevertheless, suggesting the user to inform him/-herself about existing
certificates that could be used, increases security.
What do you think?
>
>
>> Ok. I will have a look at them tonight; It seems that I then can remove
>> translations in the certhandler.cpp itself.
>>
>
> No, “None” and “Choose” are still translated.
Of course, yes.
I was thinking of two translations _("Private key") and _("Public key")
which are passed as quasi constants. But I see, they will be kept also,
well?
>
> So, in Norwegian, if we elided words using “–bork”, a translator just
> changes “%1…” into “%1–bork”.
ok. :-).
Thanks a lot.
[1] https:/
[2] https:/
Jonas G. Drange (jonas-drange) wrote : | # |
>
> > No, I don't have this opportunity.
>
> According to a script, I found for your University?! there is a cert
> chain in the script [1].
>
Oh, sorry! Great find, I'll give it a go tomorrow.
Jonas G. Drange (jonas-drange) wrote : | # |
I just made a successful connection to eduroam using the certificate provided by eduroam.
There was a lesser issue that the ItemSelector was had selectedIndex 0 after the .pem was added. This is due to bug [1] which will not be fixed by the UI toolkit. The real solution is to use a ListModel [2].
We can fix this in a subsequent branch, I don't think it's a show stopper at this point, as eduroam allows certificate-less connections.
[1] https:/
[2] http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1415
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Jonas G. Drange (jonas-drange) wrote : | # |
Thanks Joerg, for fixing the translation issues, as well as improving the cert ui. If there are no more critical issues in this branch, I want to land it as is—this means no more commits after this approve. :) This way we can get your branch into OTA5 (next system update).
JkB (joergberroth) wrote : | # |
Am Donnerstag, 2. Juli 2015 00:29:27 CEST
> after this approve. :) This way we can get your branch into OTA5
> (next system update).
>
Thats it! Thanks a lot jonas!
--
Versandt, mit Dekko von meinem Ubuntu-Gerät
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1417
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'plugins/wifi/CMakeLists.txt' |
2 | --- plugins/wifi/CMakeLists.txt 2014-09-15 15:39:50 +0000 |
3 | +++ plugins/wifi/CMakeLists.txt 2015-07-01 18:56:14 +0000 |
4 | @@ -1,6 +1,8 @@ |
5 | set(QML_SOURCES |
6 | AccessPoint.qml |
7 | BaseMenuItem.qml |
8 | +CertPicker.qml |
9 | +CertDialog.qml |
10 | Common.qml |
11 | DivMenuItem.qml |
12 | FramedMenuItem.qml |
13 | @@ -23,10 +25,12 @@ |
14 | plugin.cpp |
15 | unitymenumodelstack.cpp |
16 | previousnetworkmodel.cpp |
17 | + certhandler.cpp |
18 | wifidbushelper.h |
19 | plugin.h |
20 | unitymenumodelstack.h |
21 | previousnetworkmodel.h |
22 | + certhandler.h |
23 | nm_manager_proxy.h |
24 | nm_settings_proxy.h |
25 | nm_settings_connection_proxy.h |
26 | |
27 | === added file 'plugins/wifi/CertDialog.qml' |
28 | --- plugins/wifi/CertDialog.qml 1970-01-01 00:00:00 +0000 |
29 | +++ plugins/wifi/CertDialog.qml 2015-07-01 18:56:14 +0000 |
30 | @@ -0,0 +1,94 @@ |
31 | +import QtQuick 2.0 |
32 | +import QtQuick.Layouts 1.1 |
33 | +import Ubuntu.Components.Popups 0.1 |
34 | +import Ubuntu.Components 0.1 |
35 | +import Ubuntu.Components.ListItems 0.1 as ListItem |
36 | +import Ubuntu.SystemSettings.Wifi 1.0 |
37 | + |
38 | +Component { |
39 | + |
40 | + Dialog { |
41 | + id: certDialog |
42 | + |
43 | + property var certType; |
44 | + property var fileName; |
45 | + |
46 | + signal updateSignal(var update); |
47 | + |
48 | + anchors.fill: parent |
49 | + |
50 | + title: { |
51 | + if (certType === 0) { // certificate |
52 | + return i18n.tr("Add certificate?"); |
53 | + } else if (certType === 1) { // privatekey |
54 | + return i18n.tr("Add key?"); |
55 | + } else if (certType === 2) { // pacFile |
56 | + return i18n.tr("Add pac file?"); |
57 | + } |
58 | + } |
59 | + |
60 | + FileHandler { |
61 | + id: fileHandler |
62 | + } |
63 | + |
64 | + Label { |
65 | + id: certContentLabel |
66 | + text : i18n.tr("Content:") |
67 | + objectName: "certContentLabel" |
68 | + fontSize: "medium" |
69 | + font.bold: false |
70 | + } |
71 | + |
72 | + TextArea { |
73 | + id : certContent |
74 | + objectName: "certContent" |
75 | + readOnly: true |
76 | + width: parent.width |
77 | + autoSize: true |
78 | + maximumLineCount: 7 |
79 | + placeholderText: i18n.tr("No data available.") |
80 | + text: fileHandler.getCertContent(certDialog.fileName).toString() |
81 | + } |
82 | + |
83 | + RowLayout { |
84 | + id: buttonRow |
85 | + anchors { |
86 | + left: parent.left |
87 | + right: parent.right |
88 | + } |
89 | + spacing: units.gu(2) |
90 | + height: cancelButton.height |
91 | + |
92 | + Button { |
93 | + id: cancelButton |
94 | + Layout.fillWidth: true |
95 | + text: i18n.tr("Cancel") |
96 | + onClicked: { |
97 | + fileHandler.removeFile(certDialog.fileName); |
98 | + PopupUtils.close(certDialog); |
99 | + } |
100 | + } |
101 | + |
102 | + Button { |
103 | + id: saveButton |
104 | + text: i18n.tr("Save") |
105 | + Layout.fillWidth: true |
106 | + enabled: (certDialog.certContent.text !== "") |
107 | + onClicked: { if (certType === 0) { // certificate |
108 | + fileHandler.moveCertFile(certDialog.fileName); |
109 | + } else if (certType === 1) { // privatekey |
110 | + fileHandler.moveKeyFile(certDialog.fileName); |
111 | + } else if (certType === 2) { // pacFile |
112 | + fileHandler.movePacFile(certDialog.fileName); |
113 | + } |
114 | + |
115 | + /* Just to be sure source file will be deleted if move was |
116 | + not successfull */ |
117 | + fileHandler.removeFile(certDialog.fileName); |
118 | + certDialog.updateSignal(true); |
119 | + PopupUtils.close(certDialog); |
120 | + } |
121 | + } |
122 | + } |
123 | + } |
124 | +} |
125 | |
126 | === added file 'plugins/wifi/CertPicker.qml' |
127 | --- plugins/wifi/CertPicker.qml 1970-01-01 00:00:00 +0000 |
128 | +++ plugins/wifi/CertPicker.qml 2015-07-01 18:56:14 +0000 |
129 | @@ -0,0 +1,58 @@ |
130 | +import QtQuick 2.0 |
131 | +import QtQuick.Layouts 1.1 |
132 | +import Ubuntu.Components 0.1 |
133 | +import Ubuntu.Components.Popups 0.1 |
134 | +import Ubuntu.Content 0.1 |
135 | + |
136 | +PopupBase { |
137 | + id: picker |
138 | + |
139 | + signal fileImportSignal (var file) |
140 | + property var activeTransfer |
141 | + |
142 | + Rectangle { |
143 | + anchors.fill: parent |
144 | + |
145 | + ContentTransferHint { |
146 | + id: transferHint |
147 | + anchors.fill: parent |
148 | + activeTransfer: picker.activeTransfer |
149 | + } |
150 | + |
151 | + ContentStore { |
152 | + id: appStore |
153 | + scope: ContentScope.App |
154 | + } |
155 | + |
156 | + ContentPeerPicker { |
157 | + id: peerPicker |
158 | + anchors.fill: parent |
159 | + visible: true |
160 | + contentType: ContentType.Documents |
161 | + handler: ContentHandler.Source |
162 | + onPeerSelected: { |
163 | + peer.selectionType = ContentTransfer.Single; |
164 | + picker.activeTransfer = peer.request(appStore); |
165 | + } |
166 | + onCancelPressed: PopupUtils.close(picker) |
167 | + } |
168 | + } |
169 | + |
170 | + Connections { |
171 | + target: picker.activeTransfer ? picker.activeTransfer : null |
172 | + onStateChanged: { |
173 | + if (picker.activeTransfer.state === ContentTransfer.Charged) { |
174 | + if (picker.activeTransfer.items.length > 0) { |
175 | + var fileUrl = picker.activeTransfer.items[0].url; |
176 | + picker.fileImportSignal( |
177 | + fileUrl.toString().replace("file://", "") |
178 | + ); |
179 | + PopupUtils.close(picker); |
180 | + } |
181 | + } else if (picker.activeTransfer.state === ContentTransfer.Aborted){ |
182 | + picker.fileImportSignal(false); |
183 | + PopupUtils.close(picker); |
184 | + } |
185 | + } |
186 | + } |
187 | +} |
188 | |
189 | === modified file 'plugins/wifi/OtherNetwork.qml' |
190 | --- plugins/wifi/OtherNetwork.qml 2015-01-09 08:47:53 +0000 |
191 | +++ plugins/wifi/OtherNetwork.qml 2015-07-01 18:56:14 +0000 |
192 | @@ -31,21 +31,53 @@ |
193 | objectName: "otherNetworkDialog" |
194 | anchorToKeyboard: true |
195 | |
196 | - function settingsValid() { |
197 | - if(networkname.length == 0) { |
198 | + function settingsValid () { |
199 | + if (networkname.length === 0) { |
200 | return false; |
201 | } |
202 | - if(securityList.selectedIndex == 0) { |
203 | + if (securityList.selectedIndex === 0) { |
204 | return true |
205 | } |
206 | - if(securityList.selectedIndex == 1) { |
207 | - return password.length >= 8 |
208 | + if (securityList.selectedIndex === 3) { |
209 | + // WEP |
210 | + return password.length === 5 || |
211 | + password.length === 10 || |
212 | + password.length === 13 || |
213 | + password.length === 26; |
214 | } |
215 | - // WEP |
216 | - return password.length === 5 || |
217 | - password.length === 10 || |
218 | - password.length === 13 || |
219 | - password.length === 26; |
220 | + //WPA |
221 | + return password.length >= 8 |
222 | + } |
223 | + |
224 | + function filePicker (type) { |
225 | + var pickerDialog; |
226 | + var certDialog; |
227 | + |
228 | + pickerDialog = PopupUtils.open( |
229 | + Qt.resolvedUrl("./CertPicker.qml") |
230 | + ); |
231 | + pickerDialog.fileImportSignal.connect(function (file) { |
232 | + if (!file === false) { |
233 | + certDialogLoader.source = Qt.resolvedUrl( |
234 | + "./CertDialog.qml" |
235 | + ); |
236 | + certDialog = PopupUtils.open( |
237 | + certDialogLoader.item, authListLabel, { |
238 | + fileName: file, |
239 | + certType: type |
240 | + } |
241 | + ); |
242 | + certDialog.updateSignal.connect(function (update) { |
243 | + if (update && type === 0) { |
244 | + cacertListModel.dataupdate(); |
245 | + } else if (update && type === 1) { |
246 | + privatekeyListModel.dataupdate(); |
247 | + } else if (update && type === 2) { |
248 | + pacFileListModeL.dataupdate(); |
249 | + } |
250 | + }); |
251 | + } |
252 | + }); |
253 | } |
254 | |
255 | title: i18n.tr("Connect to Hidden Network") |
256 | @@ -67,6 +99,14 @@ |
257 | running: true |
258 | } |
259 | PropertyChanges { |
260 | + target: passwordRememberSwitch |
261 | + enabled: false |
262 | + } |
263 | + PropertyChanges { |
264 | + target: passwordRememberLabel |
265 | + opacity: 0.5 |
266 | + } |
267 | + PropertyChanges { |
268 | target: passwordVisibleSwitch |
269 | enabled: false |
270 | } |
271 | @@ -79,7 +119,103 @@ |
272 | enabled: false |
273 | } |
274 | PropertyChanges { |
275 | - target: passwordListLabel |
276 | + target: passwordLabel |
277 | + opacity: 0.5 |
278 | + } |
279 | + PropertyChanges { |
280 | + target: username |
281 | + enabled: false |
282 | + } |
283 | + PropertyChanges { |
284 | + target: usernameLabel |
285 | + opacity: 0.5 |
286 | + } |
287 | + PropertyChanges { |
288 | + target: anonymousIdentity |
289 | + enabled: false |
290 | + } |
291 | + PropertyChanges { |
292 | + target: anonymousIdentityLabel |
293 | + opacity: 0.5 |
294 | + } |
295 | + PropertyChanges { |
296 | + target: peapVersionList |
297 | + enabled: false |
298 | + opacity: 0.5 |
299 | + } |
300 | + PropertyChanges { |
301 | + target: peapVersionListLabel |
302 | + opacity: 0.5 |
303 | + } |
304 | + PropertyChanges { |
305 | + target: pacProvisioningList |
306 | + enabled: false |
307 | + opacity: 0.5 |
308 | + } |
309 | + PropertyChanges { |
310 | + target: pacProvisioningListLabel |
311 | + opacity: 0.5 |
312 | + } |
313 | + PropertyChanges { |
314 | + target: pacFileSelector |
315 | + enabled: false |
316 | + opacity: 0.5 |
317 | + } |
318 | + PropertyChanges { |
319 | + target: pacFileLabel |
320 | + opacity: 0.5 |
321 | + } |
322 | + PropertyChanges { |
323 | + target: privateKeySelector |
324 | + enabled: false |
325 | + opacity: 0.5 |
326 | + } |
327 | + PropertyChanges { |
328 | + target: privatekeyLabel |
329 | + opacity: 0.5 |
330 | + } |
331 | + PropertyChanges { |
332 | + target: usercertSelector |
333 | + enabled: false |
334 | + opacity: 0.5 |
335 | + } |
336 | + PropertyChanges { |
337 | + target: usercertLabel |
338 | + opacity: 0.5 |
339 | + } |
340 | + PropertyChanges { |
341 | + target: cacertSelector |
342 | + enabled: false |
343 | + opacity: 0.5 |
344 | + } |
345 | + PropertyChanges { |
346 | + target: cacertLabel |
347 | + opacity: 0.5 |
348 | + } |
349 | + PropertyChanges { |
350 | + target: p2authList |
351 | + enabled: false |
352 | + opacity: 0.5 |
353 | + } |
354 | + PropertyChanges { |
355 | + target: p2authListLabel |
356 | + opacity: 0.5 |
357 | + } |
358 | + PropertyChanges { |
359 | + target: authList |
360 | + enabled: false |
361 | + opacity: 0.5 |
362 | + } |
363 | + PropertyChanges { |
364 | + target: authListLabel |
365 | + opacity: 0.5 |
366 | + } |
367 | + PropertyChanges { |
368 | + target: wepInsecureLabel |
369 | + opacity: 0.5 |
370 | + } |
371 | + PropertyChanges { |
372 | + target: wepInsecureLabel |
373 | opacity: 0.5 |
374 | } |
375 | PropertyChanges { |
376 | @@ -117,10 +253,7 @@ |
377 | target: successIndicator |
378 | running: true |
379 | } |
380 | - PropertyChanges { |
381 | - target: cancelButton |
382 | - enabled: false |
383 | - } |
384 | + |
385 | PropertyChanges { |
386 | target: connectAction |
387 | enabled: false |
388 | @@ -142,7 +275,7 @@ |
389 | text : i18n.tr("Network name") |
390 | objectName: "networknameLabel" |
391 | fontSize: "medium" |
392 | - font.bold: true |
393 | + font.bold: false |
394 | color: Theme.palette.selected.backgroundText |
395 | elide: Text.ElideRight |
396 | } |
397 | @@ -150,6 +283,8 @@ |
398 | TextField { |
399 | id : networkname |
400 | objectName: "networkname" |
401 | + width: parent.width |
402 | + placeholderText: i18n.tr("SSID") |
403 | inputMethodHints: Qt.ImhNoPredictiveText |
404 | Component.onCompleted: forceActiveFocus() |
405 | } |
406 | @@ -159,7 +294,7 @@ |
407 | text : i18n.tr("Security") |
408 | objectName: "securityListLabel" |
409 | fontSize: "medium" |
410 | - font.bold: true |
411 | + font.bold: false |
412 | color: Theme.palette.selected.backgroundText |
413 | elide: Text.ElideRight |
414 | } |
415 | @@ -167,18 +302,480 @@ |
416 | ListItem.ItemSelector { |
417 | id: securityList |
418 | objectName: "securityList" |
419 | - model: [i18n.tr("None"), // index: 0 |
420 | - i18n.tr("WPA & WPA2 Personal"), // index: 1 |
421 | - i18n.tr("WEP"), // index: 2 |
422 | - ] |
423 | - } |
424 | - |
425 | - Label { |
426 | - id: passwordListLabel |
427 | - text : i18n.tr("Password") |
428 | + model: [i18n.tr("None"), // index: 0 |
429 | + i18n.tr("WPA & WPA2 Personal"), // index: 1 |
430 | + i18n.tr("WPA & WPA2 Enterprise"),// index: 2 |
431 | + i18n.tr("WEP"), // index: 3 |
432 | + i18n.tr("Dynamic WEP (802.1x)"), // index: 4 |
433 | + i18n.tr("LEAP"), // index: 5 |
434 | + ] |
435 | + selectedIndex: 1 |
436 | + } |
437 | + |
438 | + Label { |
439 | + id: wepInsecureLabel |
440 | + objectName: "wepInsecureLabel" |
441 | + color: "red" |
442 | + text: i18n.tr("This network is insecure.") |
443 | + visible: securityList.selectedIndex === 3 |
444 | + } |
445 | + |
446 | + Label { |
447 | + id: authListLabel |
448 | + text : i18n.tr("Authentication") |
449 | + objectName: "authListLabel" |
450 | + fontSize: "medium" |
451 | + font.bold: false |
452 | + color: Theme.palette.selected.backgroundText |
453 | + elide: Text.ElideRight |
454 | + visible: securityList.selectedIndex === 2 || |
455 | + securityList.selectedIndex === 4 |
456 | + } |
457 | + |
458 | + ListItem.ItemSelector { |
459 | + id: authList |
460 | + objectName: "authList" |
461 | + model: ["TLS", // index: 0 |
462 | + "TTLS", // index: 1 |
463 | + "LEAP", // index: 2 |
464 | + "FAST", // index: 3 |
465 | + "PEAP", // index: 4 |
466 | + ] |
467 | + visible: securityList.selectedIndex === 2 || |
468 | + securityList.selectedIndex === 4 |
469 | + } |
470 | + |
471 | + Label { |
472 | + id: p2authListLabel |
473 | + text : i18n.tr("Inner authentication") |
474 | + objectName: "p2authLabel" |
475 | + fontSize: "medium" |
476 | + font.bold: false |
477 | + color: Theme.palette.selected.backgroundText |
478 | + elide: Text.ElideRight |
479 | + visible: (securityList.selectedIndex === 2 || |
480 | + securityList.selectedIndex === 4 /* WPA or D-WEP */) && |
481 | + (authList.selectedIndex === 1 || |
482 | + authList.selectedIndex === 3 || |
483 | + authList.selectedIndex === 4) |
484 | + } |
485 | + |
486 | + ListItem.ItemSelector { |
487 | + id: p2authList |
488 | + objectName: "p2authList" |
489 | + width: parent.width |
490 | + model: ["PAP", // index: 0 |
491 | + "MSCHAPv2", // index: 1 |
492 | + "MSCHAP", // index: 2 |
493 | + "CHAP", // index: 3 |
494 | + "GTC", // index: 4 |
495 | + "MD5" // index: 5 |
496 | + ] |
497 | + visible: (securityList.selectedIndex === 2 || |
498 | + securityList.selectedIndex === 4 /* WPA or D-WEP */) && |
499 | + (authList.selectedIndex === 1 || |
500 | + authList.selectedIndex === 3 || |
501 | + authList.selectedIndex === 4) |
502 | + } |
503 | + |
504 | + Label { |
505 | + id: cacertLabel |
506 | + text : i18n.tr("CA certificate") |
507 | + objectName: "cacertListLabel" |
508 | + fontSize: "medium" |
509 | + font.bold: false |
510 | + color: Theme.palette.selected.backgroundText |
511 | + visible: (securityList.selectedIndex === 2 || |
512 | + securityList.selectedIndex === 4 /* WPA or D-WEP */) && |
513 | + (authList.selectedIndex === 0 || |
514 | + authList.selectedIndex === 1 || |
515 | + authList.selectedIndex === 3 || |
516 | + authList.selectedIndex === 4) |
517 | + } |
518 | + |
519 | + ListItem.ItemSelector { |
520 | + id: cacertSelector |
521 | + anchors { |
522 | + left: parent.left |
523 | + right: parent.right |
524 | + } |
525 | + visible: (securityList.selectedIndex === 2 || |
526 | + securityList.selectedIndex === 4 /* WPA or D-WEP */) && |
527 | + (authList.selectedIndex === 0 || |
528 | + authList.selectedIndex === 1 || |
529 | + authList.selectedIndex === 3 || |
530 | + authList.selectedIndex === 4) |
531 | + model: cacertListModel |
532 | + expanded: false |
533 | + delegate: certSelectorDelegate |
534 | + selectedIndex: 0 |
535 | + property string cacertFileName: { |
536 | + if (selectedIndex !== 0 && |
537 | + selectedIndex !== (cacertListModel.rowCount()-1)) { |
538 | + return cacertListModel.getfileName(selectedIndex); |
539 | + } else { |
540 | + return ""; |
541 | + } |
542 | + } |
543 | + |
544 | + onSelectedIndexChanged: { |
545 | + if (selectedIndex === cacertListModel.rowCount()-1) { |
546 | + selectedIndex = 0; |
547 | + otherNetworkDialog.filePicker(0); //Certificate |
548 | + } |
549 | + } |
550 | + } |
551 | + |
552 | + Component { |
553 | + id: certSelectorDelegate |
554 | + OptionSelectorDelegate { |
555 | + text: { |
556 | + if (CommonName.length > 32) { |
557 | + /* TRANSLATORS: %1 is the name of a certificate file. |
558 | + The ellipsis indicates that the file name has been |
559 | + truncated to 30 characters. */ |
560 | + return i18n.tr("%1…").arg(CommonName.substr(0,30)); |
561 | + } else { |
562 | + return CommonName; |
563 | + } |
564 | + } |
565 | + |
566 | + subText: { |
567 | + if (CommonName !== i18n.tr("None") && |
568 | + CommonName !== i18n.tr("Choose…")) { |
569 | + if (Organization.length > 15) { |
570 | + /* TRANSLATORS: The first position is the name of |
571 | + the organization that has issued the certificate. |
572 | + The organization name has been truncated, as |
573 | + indicated by the ellipsis. The latter position is |
574 | + the expiry date of the certificate. */ |
575 | + return i18n.tr("%1…, Exp.: %2").arg( |
576 | + Organization.substr(0,13) |
577 | + ).arg(expiryDate); |
578 | + } else { |
579 | + /* TRANSLATORS: The first position is the name of |
580 | + the organization that has issued the certificate. |
581 | + The latter position is the expiry date of the |
582 | + certificate. */ |
583 | + return i18n.tr("%1, Exp.: %2").arg(Organization) |
584 | + .arg(expiryDate); |
585 | + } |
586 | + } else { |
587 | + return ""; |
588 | + } |
589 | + } |
590 | + } |
591 | + } |
592 | + |
593 | + CertificateListModel { |
594 | + id: cacertListModel |
595 | + } |
596 | + |
597 | + Loader { |
598 | + id: certDialogLoader |
599 | + asynchronous: false |
600 | + } |
601 | + |
602 | + Label { |
603 | + id: cacertHintLabel |
604 | + text : i18n.tr("Using certificates is recommend as it increases security.") |
605 | + wrapMode: Text.WordWrap |
606 | + opacity: 0.5 |
607 | + objectName: "cacertHintLabel" |
608 | + fontSize: "small" |
609 | + font.bold: false |
610 | + color: Theme.palette.selected.backgroundText |
611 | + visible: (securityList.selectedIndex === 2 || |
612 | + securityList.selectedIndex === 4 /* WPA or D-WEP */) && |
613 | + (authList.selectedIndex === 0 || |
614 | + authList.selectedIndex === 1 || |
615 | + authList.selectedIndex === 3 || |
616 | + authList.selectedIndex === 4) && |
617 | + cacertSelector.selectedIndex === 0 |
618 | + } |
619 | + |
620 | + Label { |
621 | + id: usercertLabel |
622 | + text : i18n.tr("Client certificate") |
623 | + objectName: "usercertLabel" |
624 | + fontSize: "medium" |
625 | + font.bold: false |
626 | + color: Theme.palette.selected.backgroundText |
627 | + visible: (securityList.selectedIndex === 2 || |
628 | + securityList.selectedIndex === 4) && |
629 | + authList.selectedIndex === 0 // only for TLS |
630 | + |
631 | + } |
632 | + |
633 | + ListItem.ItemSelector { |
634 | + id: usercertSelector |
635 | + anchors { |
636 | + left: parent.left |
637 | + right: parent.right |
638 | + } |
639 | + visible: (securityList.selectedIndex === 2 || |
640 | + securityList.selectedIndex === 4) && |
641 | + authList.selectedIndex === 0 // only for TLS |
642 | + model: cacertListModel |
643 | + expanded: false |
644 | + delegate: certSelectorDelegate |
645 | + selectedIndex: 0 |
646 | + property string usercertFileName: { |
647 | + if (selectedIndex !== 0 && |
648 | + selectedIndex !== (model.rowCount()-1)) { |
649 | + return model.getfileName(selectedIndex); |
650 | + } else { |
651 | + return ""; |
652 | + } |
653 | + } |
654 | + onSelectedIndexChanged: { |
655 | + if (selectedIndex === cacertListModel.rowCount()-1) { |
656 | + selectedIndex = 0; |
657 | + otherNetworkDialog.filePicker(0); //Certificate |
658 | + } |
659 | + } |
660 | + } |
661 | + |
662 | + Label { |
663 | + id: privatekeyLabel |
664 | + text : i18n.tr("User private key") |
665 | + objectName: "userprivatekeyLabel" |
666 | + fontSize: "medium" |
667 | + font.bold: false |
668 | + color: Theme.palette.selected.backgroundText |
669 | + visible: (securityList.selectedIndex === 2 || |
670 | + securityList.selectedIndex === 4) && |
671 | + (authList.selectedIndex === 0) // only for TLS |
672 | + } |
673 | + |
674 | + PrivatekeyListModel { |
675 | + id: privatekeyListModel |
676 | + } |
677 | + |
678 | + Component{ |
679 | + id: privatekeySelectorDelegate |
680 | + OptionSelectorDelegate { |
681 | + text: KeyName |
682 | + |
683 | + subText: { |
684 | + if (KeyName !== i18n.tr("None") && |
685 | + KeyName !== i18n.tr("Choose…")) { |
686 | + /* TRANSLATORS: The first position is the type of |
687 | + private key, second the key algorithm, and third the |
688 | + length of the key in bits. */ |
689 | + return i18n.tr("%1, %2, %3 bit").arg(KeyType) |
690 | + .arg(KeyAlgorithm).arg(KeyLength); |
691 | + } else { |
692 | + return ""; |
693 | + } |
694 | + } |
695 | + } |
696 | + } |
697 | + |
698 | + ListItem.ItemSelector { |
699 | + id: privateKeySelector |
700 | + anchors { |
701 | + left: parent.left |
702 | + right: parent.right |
703 | + } |
704 | + visible: (securityList.selectedIndex === 2 || |
705 | + securityList.selectedIndex === 4) && |
706 | + (authList.selectedIndex === 0) // only for TLS |
707 | + model: privatekeyListModel |
708 | + expanded: false |
709 | + delegate: privatekeySelectorDelegate |
710 | + selectedIndex: 0 |
711 | + property string privateKeyFileName: { |
712 | + if (selectedIndex !== 0 && |
713 | + selectedIndex !== |
714 | + (model.rowCount()-1)) { |
715 | + return model.getfileName( |
716 | + selectedIndex |
717 | + ); |
718 | + } else { |
719 | + return ""; |
720 | + } |
721 | + } |
722 | + onSelectedIndexChanged: { |
723 | + if (selectedIndex === privatekeyListModel.rowCount()-1) { |
724 | + selectedIndex = 0; |
725 | + otherNetworkDialog.filePicker(1); //Key |
726 | + } |
727 | + } |
728 | + } |
729 | + |
730 | + Label { |
731 | + id: pacFileLabel |
732 | + text : i18n.tr("Pac file") |
733 | + objectName: "pacFileLabel" |
734 | + fontSize: "medium" |
735 | + font.bold: false |
736 | + color: Theme.palette.selected.backgroundText |
737 | + visible: (securityList.selectedIndex === 2 || |
738 | + securityList.selectedIndex === 4 /* WPA or D-WEP */) && |
739 | + (authList.selectedIndex === 3) |
740 | + } |
741 | + |
742 | + PacFileListModel { |
743 | + id: pacFileListModel |
744 | + } |
745 | + |
746 | + Component{ |
747 | + id: pacFileSelectorDelegate |
748 | + OptionSelectorDelegate { text: pacFileName; } |
749 | + } |
750 | + |
751 | + ListItem.ItemSelector { |
752 | + id: pacFileSelector |
753 | + anchors { |
754 | + left: parent.left |
755 | + right: parent.right |
756 | + } |
757 | + visible: (securityList.selectedIndex === 2 || |
758 | + securityList.selectedIndex === 4) && |
759 | + (authList.selectedIndex === 3) // only for FAST |
760 | + model: pacFileListModel |
761 | + expanded: false |
762 | + delegate: pacFileSelectorDelegate |
763 | + selectedIndex: 0 |
764 | + property string pacFileName: { |
765 | + if (selectedIndex !== 0 && |
766 | + selectedIndex !== (pacFileListModel.rowCount()-1)) { |
767 | + return pacFileListModel.getfileName(selectedIndex); |
768 | + } else { |
769 | + return ""; |
770 | + } |
771 | + } |
772 | + onSelectedIndexChanged: { |
773 | + if (selectedIndex === pacFileListModel.rowCount()-1) { |
774 | + selectedIndex = 0; |
775 | + otherNetworkDialog.filePicker(2); //PacFile |
776 | + } |
777 | + } |
778 | + } |
779 | + |
780 | + Label { |
781 | + id: pacProvisioningListLabel |
782 | + text : i18n.tr("Pac provisioning") |
783 | + objectName: "pacProvisioningListLabel" |
784 | + fontSize: "medium" |
785 | + font.bold: false |
786 | + color: Theme.palette.selected.backgroundText |
787 | + elide: Text.ElideRight |
788 | + visible: (securityList.selectedIndex === 2 || |
789 | + securityList.selectedIndex === 4) && |
790 | + (authList.selectedIndex === 3) |
791 | + } |
792 | + |
793 | + ListItem.ItemSelector { |
794 | + id: pacProvisioningList |
795 | + objectName: "pacProvisioningList" |
796 | + model: [i18n.tr("Disabled"), // index: 0 |
797 | + i18n.tr("Anonymous"), // index: 1 |
798 | + i18n.tr("Authenticated"), // index: 2 |
799 | + i18n.tr("Both"), // index: 3 |
800 | + ] |
801 | + selectedIndex: 1 |
802 | + visible: (securityList.selectedIndex === 2 || |
803 | + securityList.selectedIndex === 4) && |
804 | + (authList.selectedIndex === 3) |
805 | + } |
806 | + |
807 | + Label { |
808 | + id: peapVersionListLabel |
809 | + text : i18n.tr("PEAP version") |
810 | + objectName: "peapVersionListLabel" |
811 | + fontSize: "medium" |
812 | + font.bold: false |
813 | + color: Theme.palette.selected.backgroundText |
814 | + elide: Text.ElideRight |
815 | + visible: (securityList.selectedIndex === 2 || |
816 | + securityList.selectedIndex === 4) && |
817 | + (authList.selectedIndex === 4) |
818 | + } |
819 | + |
820 | + ListItem.ItemSelector { |
821 | + id: peapVersionList |
822 | + objectName: "peapVersionList" |
823 | + model: [i18n.tr("Version 0"), // index: 0 |
824 | + i18n.tr("Version 1"), // index: 1 |
825 | + i18n.tr("Automatic"), // index: 2 |
826 | + ] |
827 | + visible: (securityList.selectedIndex === 2 || |
828 | + securityList.selectedIndex === 4) && |
829 | + (authList.selectedIndex === 4) |
830 | + selectedIndex: 2 |
831 | + } |
832 | + |
833 | + Label { |
834 | + id: anonymousIdentityLabel |
835 | + text : i18n.tr("Anonymous identity") |
836 | + objectName: "anonymousIdentityLabel" |
837 | + fontSize: "medium" |
838 | + font.bold: false |
839 | + color: Theme.palette.selected.backgroundText |
840 | + visible: (securityList.selectedIndex === 2 && |
841 | + authList.selectedIndex !== 2) |
842 | + } |
843 | + |
844 | + TextField { |
845 | + id : anonymousIdentity |
846 | + objectName: "anonymousIdentity" |
847 | + width: parent.width |
848 | + visible: (securityList.selectedIndex === 2 && |
849 | + authList.selectedIndex !== 2) |
850 | + inputMethodHints: Qt.ImhNoPredictiveText |
851 | + onAccepted: { |
852 | + connectAction.trigger() |
853 | + } |
854 | + } |
855 | + |
856 | + Label { |
857 | + id: usernameLabel |
858 | + text : { |
859 | + if ((securityList.selectedIndex === 2 || |
860 | + securityList.selectedIndex === 4) && |
861 | + (authList.selectedIndex === 0 )) { |
862 | + return i18n.tr("Identity"); |
863 | + } else { |
864 | + return i18n.tr("Username"); |
865 | + } |
866 | + } |
867 | + objectName: "usernameLabel" |
868 | + fontSize: "medium" |
869 | + font.bold: false |
870 | + color: Theme.palette.selected.backgroundText |
871 | + elide: Text.ElideRight |
872 | + visible: (securityList.selectedIndex === 2 || |
873 | + securityList.selectedIndex === 4 || |
874 | + securityList.selectedIndex === 5) |
875 | + } |
876 | + |
877 | + TextField { |
878 | + id : username |
879 | + objectName: "username" |
880 | + width: parent.width |
881 | + visible: (securityList.selectedIndex === 2 || |
882 | + securityList.selectedIndex === 4 || |
883 | + securityList.selectedIndex === 5) |
884 | + inputMethodHints: Qt.ImhNoPredictiveText |
885 | + onAccepted: connectAction.trigger() |
886 | + } |
887 | + |
888 | + Label { |
889 | + id: passwordLabel |
890 | + text: { |
891 | + if ((securityList.selectedIndex === 2 || |
892 | + securityList.selectedIndex === 4) && |
893 | + (authList.selectedIndex === 0)) { |
894 | + return i18n.tr("Private key password"); |
895 | + } else { |
896 | + return i18n.tr("Password"); |
897 | + } |
898 | + } |
899 | objectName: "passwordListLabel" |
900 | fontSize: "medium" |
901 | - font.bold: true |
902 | + font.bold: false |
903 | color: Theme.palette.selected.backgroundText |
904 | elide: Text.ElideRight |
905 | visible: securityList.selectedIndex !== 0 |
906 | @@ -187,16 +784,15 @@ |
907 | TextField { |
908 | id : password |
909 | objectName: "password" |
910 | + width: parent.width |
911 | visible: securityList.selectedIndex !== 0 |
912 | echoMode: passwordVisibleSwitch.checked ? |
913 | - TextInput.Normal : TextInput.Password |
914 | + TextInput.Normal : TextInput.Password |
915 | inputMethodHints: Qt.ImhNoPredictiveText |
916 | - onAccepted: { |
917 | - connectAction.trigger(); |
918 | - } |
919 | + onAccepted: connectAction.trigger() |
920 | } |
921 | |
922 | - Row { |
923 | + Row { |
924 | id: passwordVisiblityRow |
925 | layoutDirection: Qt.LeftToRight |
926 | spacing: units.gu(2) |
927 | @@ -222,7 +818,43 @@ |
928 | } |
929 | onClicked: { |
930 | passwordVisibleSwitch.checked = |
931 | - !passwordVisibleSwitch.checked |
932 | + !passwordVisibleSwitch.checked |
933 | + } |
934 | + } |
935 | + } |
936 | + } |
937 | + |
938 | + Row { |
939 | + id: passwordRememberRow |
940 | + layoutDirection: Qt.LeftToRight |
941 | + spacing: units.gu(2) |
942 | + visible: ((securityList.selectedIndex === 2 || |
943 | + securityList.selectedIndex === 4) && |
944 | + (authList.selectedIndex === 1 || |
945 | + authList.selectedIndex === 3 || |
946 | + authList.selectedIndex === 4)) |
947 | + |
948 | + CheckBox { |
949 | + id: passwordRememberSwitch |
950 | + activeFocusOnPress: false |
951 | + } |
952 | + |
953 | + Label { |
954 | + id: passwordRememberLabel |
955 | + text : i18n.tr("Remember password") |
956 | + objectName: "passwordRememberLabel" |
957 | + fontSize: "medium" |
958 | + color: Theme.palette.selected.backgroundText |
959 | + elide: Text.ElideRight |
960 | + height: passwordRememberSwitch.height |
961 | + verticalAlignment: Text.AlignVCenter |
962 | + MouseArea { |
963 | + anchors { |
964 | + fill: parent |
965 | + } |
966 | + onClicked: { |
967 | + passwordRememberSwitch.checked = |
968 | + !passwordRememberSwitch.checked |
969 | } |
970 | } |
971 | } |
972 | @@ -289,7 +921,24 @@ |
973 | DbusHelper.connect( |
974 | networkname.text, |
975 | securityList.selectedIndex, |
976 | - password.text); |
977 | + authList.selectedIndex, |
978 | + [ |
979 | + username.text, |
980 | + anonymousIdentity.text |
981 | + ], |
982 | + [ |
983 | + password.text, |
984 | + passwordRememberSwitch.checked |
985 | + ], |
986 | + [ |
987 | + cacertSelector.cacertFileName, |
988 | + usercertSelector.usercertFileName, |
989 | + privateKeySelector.privateKeyFileName, |
990 | + pacFileSelector.pacFileName, |
991 | + pacProvisioningList.selectedIndex.toString(), |
992 | + peapVersionList.selectedIndex.toString() |
993 | + ], |
994 | + p2authList.selectedIndex); |
995 | otherNetworkDialog.state = "CONNECTING"; |
996 | } |
997 | } |
998 | @@ -321,19 +970,18 @@ |
999 | */ |
1000 | if (otherNetworkDialog.state === "CONNECTING") { |
1001 | switch (newState) { |
1002 | - case 120: |
1003 | - feedback.text = common.reasonToString(reason); |
1004 | - otherNetworkDialog.state = "FAILED"; |
1005 | - break; |
1006 | - case 100: |
1007 | - /* connection succeeded only if it was us that |
1008 | + case 120: |
1009 | + feedback.text = common.reasonToString(reason); |
1010 | + otherNetworkDialog.state = "FAILED"; |
1011 | + break; |
1012 | + case 100: |
1013 | + /* connection succeeded only if it was us that |
1014 | created it */ |
1015 | - otherNetworkDialog.state = "SUCCEEDED"; |
1016 | - break; |
1017 | + otherNetworkDialog.state = "SUCCEEDED"; |
1018 | + break; |
1019 | } |
1020 | } |
1021 | } |
1022 | } |
1023 | } |
1024 | } |
1025 | - |
1026 | |
1027 | === added file 'plugins/wifi/certhandler.cpp' |
1028 | --- plugins/wifi/certhandler.cpp 1970-01-01 00:00:00 +0000 |
1029 | +++ plugins/wifi/certhandler.cpp 2015-07-01 18:56:14 +0000 |
1030 | @@ -0,0 +1,348 @@ |
1031 | +#include "certhandler.h" |
1032 | + |
1033 | +#include <QtQml> |
1034 | +#include <QtQml/QQmlContext> |
1035 | +#include <QtDebug> |
1036 | +#include <QObject> |
1037 | +#include <QSslCertificate> |
1038 | +#include <QSslKey> |
1039 | +#include <QAbstractListModel> |
1040 | +#include <QDir> |
1041 | +#include <QFile> |
1042 | + |
1043 | + |
1044 | +QString appPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); |
1045 | +#define CERTS_PATH appPath +"/wifi/ssl/certs/" |
1046 | +#define KEYS_PATH appPath +"/wifi/ssl/private/" |
1047 | +#define PACS_PATH appPath +"/wifi/ssl/pac/" |
1048 | + |
1049 | +#include <libintl.h> |
1050 | +QString _(const char *text){ |
1051 | + return QString::fromUtf8(dgettext(0, text)); |
1052 | +} |
1053 | + |
1054 | +QByteArray FileHandler::getCertContent(QString filename){ |
1055 | + QFile file(filename); |
1056 | + if (!file.open(QIODevice::ReadOnly)) { |
1057 | + qWarning() << "Could not resolve File (" << filename << "): File does not exist or is empty." ; |
1058 | + return QByteArray(); |
1059 | + } |
1060 | + else { |
1061 | + return file.readAll(); |
1062 | + } |
1063 | +} |
1064 | + |
1065 | +QString FileHandler::moveCertFile(QString filename){ |
1066 | + QDir certPath(CERTS_PATH); |
1067 | + if (!certPath.exists(CERTS_PATH)){ |
1068 | + certPath.mkpath(CERTS_PATH); |
1069 | + } |
1070 | + QFile file(filename); |
1071 | + QByteArray certificate = getCertContent(filename); |
1072 | + QList<QSslCertificate> SslCertificateList = QSslCertificate::fromData(certificate, QSsl::Pem); |
1073 | + if ( !SslCertificateList.isEmpty() ){ |
1074 | + QStringList subject = SslCertificateList[0].subjectInfo(QSslCertificate::CommonName); |
1075 | + QString modFileName = CERTS_PATH+subject[0]+".pem"; |
1076 | + if(file.rename(modFileName.replace(" ", "_"))){ |
1077 | + return file.fileName(); |
1078 | + } else { |
1079 | + return ""; |
1080 | + } |
1081 | + } |
1082 | + return ""; |
1083 | +} |
1084 | + |
1085 | +QString FileHandler::moveKeyFile(QString filename){ |
1086 | + QDir keyPath(KEYS_PATH); |
1087 | + if (!keyPath.exists(KEYS_PATH)){ |
1088 | + keyPath.mkpath(KEYS_PATH); |
1089 | + } |
1090 | + QFile file(filename); |
1091 | + file.open(QIODevice::ReadOnly); |
1092 | + QSslKey checkKey(file.readAll(), QSsl::Rsa); |
1093 | + file.close(); |
1094 | + if ( !checkKey.isNull() ){ |
1095 | + QFileInfo fileInfo(file); |
1096 | + QString modFileName = KEYS_PATH + fileInfo.fileName().replace(" ", "_"); |
1097 | + if(file.rename(modFileName)){ |
1098 | + return file.fileName(); |
1099 | + } else { |
1100 | + return "" ; |
1101 | + } |
1102 | + } |
1103 | + return ""; |
1104 | +} |
1105 | + |
1106 | +QString FileHandler::movePacFile(QString filename){ |
1107 | + QDir keyPath(PACS_PATH); |
1108 | + if (!keyPath.exists(PACS_PATH)){ |
1109 | + keyPath.mkpath(PACS_PATH); |
1110 | + } |
1111 | + QFile file(filename); |
1112 | + QFileInfo fileInfo(file); |
1113 | + QString modFileName = PACS_PATH + fileInfo.baseName().replace(" ", "_") + ".pac"; |
1114 | + if(file.rename(modFileName)){ |
1115 | + return file.fileName(); |
1116 | + } |
1117 | + return "" ; |
1118 | +} |
1119 | + |
1120 | +bool FileHandler::removeFile(QString filename){ |
1121 | + QFile file(filename); |
1122 | + return file.remove(); |
1123 | +} |
1124 | + |
1125 | +struct CertificateListModel::Private { |
1126 | + QStringList data; |
1127 | +}; |
1128 | + |
1129 | +CertificateListModel::CertificateListModel(QObject *parent) : QAbstractListModel(parent) { |
1130 | + p = new CertificateListModel::Private(); |
1131 | + QStringList nameFilter("*.pem"); |
1132 | + QDir directory(CERTS_PATH); |
1133 | + QStringList files = directory.entryList(nameFilter); |
1134 | + files.sort(Qt::CaseInsensitive); |
1135 | + files.insert(0, _("None") ); |
1136 | + files.append( _("Choose…") ); |
1137 | + p->data = files; |
1138 | +} |
1139 | + |
1140 | +CertificateListModel::~CertificateListModel() { |
1141 | + delete p; |
1142 | +} |
1143 | + |
1144 | +QHash<int, QByteArray> CertificateListModel::roleNames() const { |
1145 | + QHash<int, QByteArray> roles; |
1146 | + roles[CNRole] = "CommonName"; |
1147 | + roles[ORole] = "Organization"; |
1148 | + roles[expDateRole] = "expiryDate"; |
1149 | + |
1150 | + //roles[certFileNameRole] = "certFileName"; |
1151 | + //...more if needed see QSslCertificate::SubjectInfo |
1152 | + return roles; |
1153 | +} |
1154 | + |
1155 | +int CertificateListModel::rowCount(const QModelIndex &/*parent*/) const { |
1156 | + return p->data.size(); |
1157 | +} |
1158 | + |
1159 | +QString CertificateListModel::getfileName(const int selectedIndex) const { |
1160 | + return CERTS_PATH + p->data[selectedIndex]; |
1161 | +} |
1162 | + |
1163 | +void CertificateListModel::dataupdate(){ |
1164 | + beginResetModel(); |
1165 | + p->data.clear(); |
1166 | + QStringList nameFilter("*.pem"); |
1167 | + QDir directory(CERTS_PATH); |
1168 | + QStringList files = directory.entryList(nameFilter); |
1169 | + files.sort(Qt::CaseInsensitive); |
1170 | + files.insert(0, _("None") ); |
1171 | + files.append( _("Choose…") ); |
1172 | + p->data = files; |
1173 | + endResetModel(); |
1174 | +} |
1175 | + |
1176 | +QVariant CertificateListModel::data(const QModelIndex &index, int role) const { |
1177 | + if(!index.isValid() || index.row() >= ( p->data.size()) ) { |
1178 | + return QVariant(); |
1179 | + } else if (index.row() == 0){ |
1180 | + const QString &row0 = p->data[index.row()]; |
1181 | + |
1182 | + switch(role) { |
1183 | + case CNRole : return row0; |
1184 | + case ORole : return ""; |
1185 | + case expDateRole : return ""; |
1186 | + |
1187 | + } |
1188 | + } else if (index.row() == p->data.size()-1){ |
1189 | + const QString &rowend = p->data[index.row()]; |
1190 | + |
1191 | + switch(role) { |
1192 | + case CNRole : return rowend; |
1193 | + case ORole : return ""; |
1194 | + case expDateRole : return ""; |
1195 | + } |
1196 | + } |
1197 | + |
1198 | + const QString &row = CERTS_PATH+p->data[index.row()]; |
1199 | + QList<QSslCertificate> certificate = QSslCertificate::fromPath(row, QSsl::Pem, QRegExp::Wildcard); |
1200 | + |
1201 | + switch(role) { |
1202 | + |
1203 | + case CNRole : return certificate[0].subjectInfo(QSslCertificate::CommonName)[0]; |
1204 | + case ORole : return certificate[0].subjectInfo(QSslCertificate::Organization)[0]; |
1205 | + case expDateRole : return certificate[0].expiryDate().toString("dd.MM.yyyy"); |
1206 | + |
1207 | + default : return QVariant(); |
1208 | + } |
1209 | +} |
1210 | + |
1211 | +/***************************************/ |
1212 | + |
1213 | +struct PrivatekeyListModel::Private { |
1214 | + QStringList data; |
1215 | +}; |
1216 | + |
1217 | +PrivatekeyListModel::PrivatekeyListModel(QObject *parent) : QAbstractListModel(parent) { |
1218 | + p = new PrivatekeyListModel::Private(); |
1219 | + QDir directory(KEYS_PATH); |
1220 | + QStringList files = directory.entryList(QDir::Files, QDir::Name); |
1221 | + files.sort(Qt::CaseInsensitive); |
1222 | + files.insert(0, _("None") ); |
1223 | + files.append( _("Choose…") ); |
1224 | + p->data = files; |
1225 | +} |
1226 | + |
1227 | +PrivatekeyListModel::~PrivatekeyListModel() { |
1228 | + delete p; |
1229 | +} |
1230 | + |
1231 | +QHash<int, QByteArray> PrivatekeyListModel::roleNames() const { |
1232 | + QHash<int, QByteArray> roles; |
1233 | + roles[keyName] = "KeyName"; |
1234 | + roles[keyType] = "KeyType"; |
1235 | + roles[keyAlgorithm] = "KeyAlgorithm"; |
1236 | + roles[keyLength] = "KeyLength"; |
1237 | + return roles; |
1238 | +} |
1239 | + |
1240 | +int PrivatekeyListModel::rowCount(const QModelIndex &/*parent*/) const { |
1241 | + return p->data.size(); |
1242 | +} |
1243 | + |
1244 | +QString PrivatekeyListModel::getfileName(const int selectedIndex) const { |
1245 | + return KEYS_PATH + p->data[selectedIndex]; |
1246 | +} |
1247 | + |
1248 | +void PrivatekeyListModel::dataupdate(){ |
1249 | + beginResetModel(); |
1250 | + p->data.clear(); |
1251 | + QDir directory(KEYS_PATH); |
1252 | + QStringList files = directory.entryList(QDir::Files, QDir::Name); |
1253 | + files.sort(Qt::CaseInsensitive); |
1254 | + files.insert(0, _("None") ); |
1255 | + files.append( _("Choose…") ); |
1256 | + p->data = files; |
1257 | + endResetModel(); |
1258 | +} |
1259 | + |
1260 | +QVariant PrivatekeyListModel::data(const QModelIndex &index, int role) const { |
1261 | + if(!index.isValid() || index.row() >= ( p->data.size()) ) { |
1262 | + return QVariant(); |
1263 | + } else if (index.row() == 0){ |
1264 | + const QString &row0 = p->data[index.row()]; |
1265 | + |
1266 | + switch(role) { |
1267 | + case keyName : return row0; // returns "None" |
1268 | + case keyType : return ""; |
1269 | + case keyAlgorithm : return ""; |
1270 | + case keyLength : return ""; |
1271 | + } |
1272 | + } else if (index.row() == p->data.size()-1){ |
1273 | + const QString &rowend = p->data[index.row()]; |
1274 | + |
1275 | + switch(role) { |
1276 | + case keyName : return rowend; // returns "Choose file... |
1277 | + case keyType : return ""; |
1278 | + case keyAlgorithm : return ""; |
1279 | + case keyLength : return ""; |
1280 | + } |
1281 | + } |
1282 | + |
1283 | + const QString &row = KEYS_PATH+p->data[index.row()]; |
1284 | + QFile keyFile(row); |
1285 | + keyFile.open(QIODevice::ReadOnly); |
1286 | + QSslKey privateKey( keyFile.readAll(), QSsl::Rsa ); |
1287 | + QString type; |
1288 | + if (privateKey.type() == 0){ type = _("Private key");} |
1289 | + else { type = _("Public key"); } |
1290 | + |
1291 | + QString algorithm; |
1292 | + if (privateKey.algorithm() == 1) { algorithm = "RSA";} |
1293 | + else if (privateKey.algorithm() == 2){ algorithm = "DSA";} |
1294 | + else { algorithm = _("Opaque");} |
1295 | + |
1296 | + QFileInfo keyFileInfo(keyFile); |
1297 | + switch(role) { |
1298 | + |
1299 | + case keyName : return keyFileInfo.fileName(); |
1300 | + case keyType : return type; |
1301 | + case keyAlgorithm : return algorithm; |
1302 | + case keyLength : return privateKey.length(); |
1303 | + |
1304 | + default : return QVariant(); |
1305 | + } |
1306 | +} |
1307 | + |
1308 | +/***************************************/ |
1309 | + |
1310 | +struct PacFileListModel::Private { |
1311 | + QStringList data; |
1312 | +}; |
1313 | + |
1314 | +PacFileListModel::PacFileListModel(QObject *parent) : QAbstractListModel(parent) { |
1315 | + p = new PacFileListModel::Private(); |
1316 | + QDir directory(PACS_PATH); |
1317 | + QStringList files = directory.entryList(QDir::Files, QDir::Name); |
1318 | + files.sort(Qt::CaseInsensitive); |
1319 | + files.insert(0, _("None") ); |
1320 | + files.append( _("Choose…") ); |
1321 | + p->data = files; |
1322 | +} |
1323 | + |
1324 | +PacFileListModel::~PacFileListModel() { |
1325 | + delete p; |
1326 | +} |
1327 | + |
1328 | +QHash<int, QByteArray> PacFileListModel::roleNames() const { |
1329 | + QHash<int, QByteArray> roles; |
1330 | + roles[pacFileName] = "pacFileName"; |
1331 | + return roles; |
1332 | +} |
1333 | + |
1334 | +int PacFileListModel::rowCount(const QModelIndex &/*parent*/) const { |
1335 | + return p->data.size(); |
1336 | +} |
1337 | + |
1338 | +QString PacFileListModel::getfileName(const int selectedIndex) const { |
1339 | + return PACS_PATH + p->data[selectedIndex]; |
1340 | +} |
1341 | + |
1342 | +void PacFileListModel::dataupdate(){ |
1343 | + beginResetModel(); |
1344 | + p->data.clear(); |
1345 | + QDir directory(PACS_PATH); |
1346 | + QStringList files = directory.entryList(QDir::Files, QDir::Name); |
1347 | + files.sort(Qt::CaseInsensitive); |
1348 | + files.insert(0, _("None") ); |
1349 | + files.append( _("Choose…") ); |
1350 | + p->data = files; |
1351 | + endResetModel(); |
1352 | +} |
1353 | + |
1354 | +QVariant PacFileListModel::data(const QModelIndex &index, int role) const { |
1355 | + if(!index.isValid() || index.row() >= ( p->data.size()) ) { |
1356 | + return QVariant(); |
1357 | + } else if (index.row() == 0){ |
1358 | + const QString &row0 = p->data[index.row()]; |
1359 | + |
1360 | + switch(role) { |
1361 | + case pacFileName : return row0; // returns "None" |
1362 | + } |
1363 | + } else if (index.row() == p->data.size()-1){ |
1364 | + const QString &rowend = p->data[index.row()]; |
1365 | + |
1366 | + switch(role) { |
1367 | + case pacFileName : return rowend; // returns "Choose file... |
1368 | + } |
1369 | + } |
1370 | + |
1371 | + const QString &name = p->data[index.row()]; |
1372 | + switch(role) { |
1373 | + |
1374 | + case pacFileName : return name; |
1375 | + |
1376 | + default : return QVariant(); |
1377 | + } |
1378 | +} |
1379 | |
1380 | === added file 'plugins/wifi/certhandler.h' |
1381 | --- plugins/wifi/certhandler.h 1970-01-01 00:00:00 +0000 |
1382 | +++ plugins/wifi/certhandler.h 2015-07-01 18:56:14 +0000 |
1383 | @@ -0,0 +1,98 @@ |
1384 | +#ifndef CERTHANDLER_H |
1385 | +#define CERTHANDLER_H |
1386 | + |
1387 | +#include <QtQml> |
1388 | +#include <QtQml/QQmlContext> |
1389 | +#include <QObject> |
1390 | +#include <QAbstractListModel> |
1391 | + |
1392 | +class FileHandler : public QObject |
1393 | +{ |
1394 | + Q_OBJECT |
1395 | +public: |
1396 | + Q_INVOKABLE QByteArray getCertContent(QString filename); |
1397 | + Q_INVOKABLE QString moveCertFile(QString filename); |
1398 | + Q_INVOKABLE QString moveKeyFile(QString filename); |
1399 | + Q_INVOKABLE QString movePacFile(QString filename); |
1400 | + Q_INVOKABLE bool removeFile(QString filename); |
1401 | +}; |
1402 | + |
1403 | + |
1404 | + |
1405 | +class CertificateListModel : public QAbstractListModel |
1406 | +{ |
1407 | + Q_OBJECT |
1408 | + |
1409 | +public: |
1410 | + enum CertificateListRoles { |
1411 | + CNRole = Qt::UserRole + 1, |
1412 | + ORole, |
1413 | + expDateRole, |
1414 | + //certFileNameRole, |
1415 | + }; |
1416 | + |
1417 | + explicit CertificateListModel(QObject *parent = 0); |
1418 | + ~CertificateListModel(); |
1419 | + QHash<int, QByteArray> roleNames() const; |
1420 | + Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const; |
1421 | + Q_INVOKABLE QString getfileName(const int selectedIndex) const; |
1422 | + Q_INVOKABLE void dataupdate(); |
1423 | + QVariant data(const QModelIndex &index, int role) const; |
1424 | + |
1425 | +private: |
1426 | + struct Private; |
1427 | + Private *p; |
1428 | + |
1429 | +}; |
1430 | + |
1431 | +class PrivatekeyListModel : public QAbstractListModel |
1432 | +{ |
1433 | + Q_OBJECT |
1434 | + |
1435 | +public: |
1436 | + enum PrivatekeyListRoles { |
1437 | + keyName = Qt::UserRole + 1, |
1438 | + keyType, |
1439 | + keyAlgorithm, |
1440 | + keyLength, |
1441 | + }; |
1442 | + |
1443 | + explicit PrivatekeyListModel(QObject *parent = 0); |
1444 | + ~PrivatekeyListModel(); |
1445 | + QHash<int, QByteArray> roleNames() const; |
1446 | + Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const; |
1447 | + Q_INVOKABLE QString getfileName(const int selectedIndex) const; |
1448 | + Q_INVOKABLE void dataupdate(); |
1449 | + QVariant data(const QModelIndex &index, int role) const; |
1450 | +private: |
1451 | + struct Private; |
1452 | + Private *p; |
1453 | + |
1454 | +}; |
1455 | + |
1456 | +class PacFileListModel : public QAbstractListModel |
1457 | +{ |
1458 | + Q_OBJECT |
1459 | + |
1460 | +public: |
1461 | + enum PacFileListRoles { |
1462 | + pacFileName = Qt::UserRole + 1, |
1463 | + |
1464 | + }; |
1465 | + |
1466 | + explicit PacFileListModel(QObject *parent = 0); |
1467 | + ~PacFileListModel(); |
1468 | + QHash<int, QByteArray> roleNames() const; |
1469 | + Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const; |
1470 | + Q_INVOKABLE QString getfileName(const int selectedIndex) const; |
1471 | + Q_INVOKABLE void dataupdate(); |
1472 | + QVariant data(const QModelIndex &index, int role) const; |
1473 | +private: |
1474 | + struct Private; |
1475 | + Private *p; |
1476 | + |
1477 | +}; |
1478 | + |
1479 | + |
1480 | +#endif // CERTHANDLER_H |
1481 | + |
1482 | |
1483 | === modified file 'plugins/wifi/plugin.cpp' |
1484 | --- plugins/wifi/plugin.cpp 2014-07-23 13:53:48 +0000 |
1485 | +++ plugins/wifi/plugin.cpp 2015-07-01 18:56:14 +0000 |
1486 | @@ -22,6 +22,7 @@ |
1487 | #include "unitymenumodelstack.h" |
1488 | #include "wifidbushelper.h" |
1489 | #include "previousnetworkmodel.h" |
1490 | +#include "certhandler.h" |
1491 | |
1492 | namespace { |
1493 | |
1494 | @@ -45,6 +46,10 @@ |
1495 | qmlRegisterType<UnityMenuModelStack>(uri, 1, 0, "UnityMenuModelStack"); |
1496 | qmlRegisterSingletonType<WifiDbusHelper>(uri, 1, 0, "DbusHelper", dbusProvider); |
1497 | qmlRegisterType<PreviousNetworkModel>(uri, 1, 0, "PreviousNetworkModel"); |
1498 | + qmlRegisterType<CertificateListModel>(uri, 1, 0, "CertificateListModel"); |
1499 | + qmlRegisterType<PrivatekeyListModel>(uri, 1, 0, "PrivatekeyListModel"); |
1500 | + qmlRegisterType<PacFileListModel>(uri, 1, 0, "PacFileListModel"); |
1501 | + qmlRegisterType<FileHandler>(uri, 1, 0, "FileHandler"); |
1502 | } |
1503 | |
1504 | void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
1505 | |
1506 | === modified file 'plugins/wifi/wifidbushelper.cpp' |
1507 | --- plugins/wifi/wifidbushelper.cpp 2015-04-07 20:45:40 +0000 |
1508 | +++ plugins/wifi/wifidbushelper.cpp 2015-07-01 18:56:14 +0000 |
1509 | @@ -40,14 +40,14 @@ |
1510 | Q_DECLARE_METATYPE(ConfigurationData) |
1511 | |
1512 | WifiDbusHelper::WifiDbusHelper(QObject *parent) : QObject(parent), |
1513 | - m_systemBusConnection(QDBusConnection::systemBus()) |
1514 | + m_systemBusConnection(QDBusConnection::systemBus()) |
1515 | { |
1516 | qDBusRegisterMetaType<ConfigurationData>(); |
1517 | } |
1518 | |
1519 | -void WifiDbusHelper::connect(QString ssid, int security, QString password) |
1520 | +void WifiDbusHelper::connect(QString ssid, int security, int auth, QStringList usernames, QStringList password, QStringList certs, int p2auth) |
1521 | { |
1522 | - if(security<0 || security>2) { |
1523 | + if((security<0 || security>5) || (auth<0 || auth>4) || (p2auth<0 || p2auth>5)) { |
1524 | qWarning() << "Qml and C++ have gotten out of sync. Can't connect.\n"; |
1525 | return; |
1526 | } |
1527 | @@ -68,26 +68,116 @@ |
1528 | // security: |
1529 | // 0: None |
1530 | // 1: WPA & WPA2 Personal |
1531 | - // 2: WEP |
1532 | - if (security != 0) { |
1533 | + // 2: WPA Enterprise |
1534 | + // 3: WEP |
1535 | + // 4: Dynamic WEP |
1536 | + // 5: LEAP |
1537 | + if (security != 0) { // WPA Enterprise or Dynamic WEP |
1538 | wireless["security"] = QStringLiteral("802-11-wireless-security"); |
1539 | |
1540 | QVariantMap wireless_security; |
1541 | |
1542 | if (security == 1) { |
1543 | wireless_security["key-mgmt"] = QStringLiteral("wpa-psk"); |
1544 | - wireless_security["psk"] = password; |
1545 | - } else if (security == 2) { |
1546 | + wireless_security["psk"] = password[0]; |
1547 | + } else if (security == 3) { |
1548 | wireless_security["key-mgmt"] = QStringLiteral("none"); |
1549 | wireless_security["auth-alg"] = QStringLiteral("open"); |
1550 | - wireless_security["wep-key0"] = password; |
1551 | + wireless_security["wep-key0"] = password[0]; |
1552 | wireless_security["wep-key-type"] = QVariant(uint(1)); |
1553 | + } else if (security == 2) { |
1554 | + wireless_security["key-mgmt"] = QStringLiteral("wpa-eap"); |
1555 | + } else if (security == 4) { |
1556 | + wireless_security["key-mgmt"] = QStringLiteral("ieee8021x"); |
1557 | + /* leave disabled as hopefully not needed: |
1558 | + QStringList wep_pairwise, wep_group; |
1559 | + wep_pairwise[0] ="wep40"; wep_pairwise[1] ="wep104"; |
1560 | + wep_group[0] ="wep40"; wep_group[1] ="wep104"; |
1561 | + wireless_security["pairwise"] = wep_pairwise; |
1562 | + wireless_security["group"] = wep_group; */ |
1563 | + } else if (security == 5) { |
1564 | + wireless_security["key-mgmt"] = QStringLiteral("ieee8021x"); |
1565 | + wireless_security["auth-alg"] = QStringLiteral("leap"); |
1566 | + wireless_security["leap-username"] = usernames[0]; |
1567 | + wireless_security["leap-password"] = password[0]; |
1568 | } |
1569 | configuration["802-11-wireless-security"] = wireless_security; |
1570 | } |
1571 | |
1572 | configuration["802-11-wireless"] = wireless; |
1573 | |
1574 | + if (security == 2 || security == 4){ |
1575 | + |
1576 | + QVariantMap wireless_802_1x; |
1577 | + // [802-1x] |
1578 | + /*TLS // index: 0 |
1579 | + TTLS // index: 1 |
1580 | + LEAP // index: 2 |
1581 | + FAST // index: 3 |
1582 | + PEAP // index: 4 */ |
1583 | + wireless_802_1x["identity"] = usernames[0]; |
1584 | + if (auth != 0) { |
1585 | + wireless_802_1x["password"] = password[0]; |
1586 | + } |
1587 | + |
1588 | + QByteArray cacert( "file://" + certs[0].toUtf8() + '\0'); |
1589 | + QByteArray clientcert("file://" + certs[1].toUtf8() + '\0'); |
1590 | + QByteArray privatekey("file://" + certs[2].toUtf8() + '\0'); |
1591 | + QString pacFile( certs[3] ); |
1592 | + |
1593 | + if (auth == 0) { // TLS |
1594 | + wireless_802_1x["eap"] = QStringList("tls"); |
1595 | + if (certs[0] != "") {wireless_802_1x["ca-cert"] = cacert;} |
1596 | + if (certs[1] != "") {wireless_802_1x["client-cert"] = clientcert;} |
1597 | + if (certs[2] != "") {wireless_802_1x["private-key"] = privatekey;} |
1598 | + wireless_802_1x["private-key-password"] = password[0]; |
1599 | + } else if (auth == 1) { // TTLS |
1600 | + wireless_802_1x["eap"] = QStringList("ttls"); |
1601 | + if (certs[0] != "") {wireless_802_1x["ca-cert"] = cacert;} |
1602 | + if (usernames[1] != "") {wireless_802_1x["anonymous-identity"] = usernames[1];} |
1603 | + if (password[1] == "false") {wireless_802_1x["password-flags"] = uint(2);} |
1604 | + } else if (auth == 2) { // LEAP |
1605 | + wireless_802_1x["eap"] = QStringList("leap"); |
1606 | + } else if (auth == 3) { // FAST |
1607 | + wireless_802_1x["eap"] = QStringList("fast"); |
1608 | + if (certs[0] != "") {wireless_802_1x["ca-cert"] = cacert;} |
1609 | + if (usernames[1] != "") {wireless_802_1x["anonymous-identity"] = usernames[1];} |
1610 | + if (password[1] == "false") {wireless_802_1x["password-flags"] = uint(2);} |
1611 | + if (certs[3] != "" ) {wireless_802_1x["pac-file"] = pacFile;} |
1612 | + wireless_802_1x["phase1-fast-provisioning"] = certs[4]; |
1613 | + } else if (auth == 4) { // PEAP |
1614 | + wireless_802_1x["eap"] = QStringList("peap"); |
1615 | + if (certs[0] != "") {wireless_802_1x["ca-cert"] = cacert;} |
1616 | + if (usernames[1] != "") {wireless_802_1x["anonymous-identity"] = usernames[1];} |
1617 | + if (password[1] == "false") {wireless_802_1x["password-flags"] = uint(2);} |
1618 | + if (certs[5] != "2") {wireless_802_1x["phase1-peapver"] = certs[5]; } |
1619 | + // wireless_802_1x["phase1-peaplabel"] = QString("1"); #jkb:let us unset this until problems are reported. |
1620 | + } |
1621 | + |
1622 | + if (auth == 1 || auth == 3 || auth == 4 ){ // only for TTLS, FAST and PEAP |
1623 | + /* PAP // index: 0 |
1624 | + MSCHAPv2 // index: 1 |
1625 | + MSCHAP // index: 2 |
1626 | + CHAP // index: 3 |
1627 | + GTC // index: 4 |
1628 | + MD5 // index: 5 */ |
1629 | + if (p2auth == 0) { |
1630 | + wireless_802_1x["phase2-auth"] = QStringLiteral("pap"); |
1631 | + } else if (p2auth == 1) { |
1632 | + wireless_802_1x["phase2-auth"] = QStringLiteral("mschapv2"); |
1633 | + } else if (p2auth == 2) { |
1634 | + wireless_802_1x["phase2-auth"] = QStringLiteral("mschap"); |
1635 | + } else if (p2auth == 3) { |
1636 | + wireless_802_1x["phase2-auth"] = QStringLiteral("chap"); |
1637 | + } else if (p2auth == 4) { |
1638 | + wireless_802_1x["phase2-auth"] = QStringLiteral("gtc"); |
1639 | + } else if (p2auth == 5) { |
1640 | + wireless_802_1x["phase2-auth"] = QStringLiteral("md5"); |
1641 | + } |
1642 | + } |
1643 | + configuration["802-1x"] = wireless_802_1x; |
1644 | + } |
1645 | + |
1646 | // find the first wlan adapter for now |
1647 | auto reply1 = mgr.GetDevices(); |
1648 | reply1.waitForFinished(); |
1649 | @@ -184,9 +274,10 @@ |
1650 | } |
1651 | } |
1652 | |
1653 | + |
1654 | void WifiDbusHelper::nmDeviceStateChanged(uint newState, |
1655 | - uint oldState, |
1656 | - uint reason) |
1657 | + uint oldState, |
1658 | + uint reason) |
1659 | { |
1660 | Q_UNUSED (oldState); |
1661 | Q_EMIT (deviceStateChanged(newState, reason)); |
1662 | @@ -329,15 +420,22 @@ |
1663 | // If the connection has never been activated succesfully there is a |
1664 | // high chance that it has no stored secrects. |
1665 | if (timestamp != 0) { |
1666 | - auto reply = m_iface.GetSecrets("802-11-wireless-security"); |
1667 | + |
1668 | + QString secretsType; |
1669 | + if (keymgmt == "wpa-psk" && authalg == "open") { |
1670 | + secretsType = "802-11-wireless-security"; |
1671 | + } else if (keymgmt == "wpa-eap" || keymgmt == "ieee8021x") { |
1672 | + secretsType = "802-1x"; |
1673 | + } |
1674 | + |
1675 | + auto reply = m_iface.GetSecrets( secretsType ); |
1676 | reply.waitForFinished(); |
1677 | if(!reply.isValid()) { |
1678 | qWarning() << "Error querying secrects: " << reply.error().message() << "\n"; |
1679 | return; |
1680 | } |
1681 | auto secrects = reply.value(); |
1682 | - |
1683 | - auto match = secrects.find("802-11-wireless-security"); |
1684 | + auto match = secrects.find( secretsType ); |
1685 | if (match != secrects.end()) { |
1686 | auto secrects_security = *match; |
1687 | |
1688 | @@ -345,6 +443,8 @@ |
1689 | password = secrects_security["wep-key0"].toString(); |
1690 | } else if (keymgmt == "wpa-psk" && authalg == "open") { |
1691 | password = secrects_security["psk"].toString(); |
1692 | + } else if (keymgmt == "wpa-eap" || keymgmt == "ieee8021x") { |
1693 | + password = secrects_security["password"].toString(); |
1694 | } else { |
1695 | } |
1696 | } |
1697 | @@ -390,7 +490,7 @@ |
1698 | QList<QStringList> WifiDbusHelper::getPreviouslyConnectedWifiNetworks() { |
1699 | QList<QStringList> networks; |
1700 | |
1701 | - OrgFreedesktopNetworkManagerSettingsInterface foo |
1702 | + OrgFreedesktopNetworkManagerSettingsInterface foo |
1703 | (NM_SERVICE, |
1704 | "/org/freedesktop/NetworkManager/Settings", |
1705 | QDBusConnection::systemBus()); |
1706 | @@ -464,7 +564,7 @@ |
1707 | auto ac_path_var = iface.property("ActiveConnection"); |
1708 | if(!ac_path_var.isValid()) { |
1709 | qWarning() << __PRETTY_FUNCTION__ << ": Could not get active connection property from " |
1710 | - << d.path() << ".\n"; |
1711 | + << d.path() << ".\n"; |
1712 | return true; |
1713 | } |
1714 | QString ac_path = ac_path_var.value<QDBusObjectPath>().path(); |
1715 | @@ -472,7 +572,7 @@ |
1716 | auto conn_path_var = ac_iface.property("Connection"); |
1717 | if(!conn_path_var.isValid()) { |
1718 | qWarning() << __PRETTY_FUNCTION__ << ": Could not get connection path property from " |
1719 | - << ac_path << ".\n"; |
1720 | + << ac_path << ".\n"; |
1721 | return false; |
1722 | } |
1723 | forgetConnection(conn_path_var.value<QDBusObjectPath>().path()); |
1724 | |
1725 | === modified file 'plugins/wifi/wifidbushelper.h' |
1726 | --- plugins/wifi/wifidbushelper.h 2014-10-10 14:10:52 +0000 |
1727 | +++ plugins/wifi/wifidbushelper.h 2015-07-01 18:56:14 +0000 |
1728 | @@ -37,7 +37,7 @@ |
1729 | explicit WifiDbusHelper(QObject *parent = nullptr); |
1730 | ~WifiDbusHelper() {}; |
1731 | |
1732 | - Q_INVOKABLE void connect(QString ssid, int security, QString password); |
1733 | + Q_INVOKABLE void connect(QString ssid, int security, int auth, QStringList usernames, QStringList password, QStringList certs, int p2auth); |
1734 | Q_INVOKABLE QList<QStringList> getPreviouslyConnectedWifiNetworks(); |
1735 | Q_INVOKABLE void forgetConnection(const QString dbus_path); |
1736 | Q_INVOKABLE bool forgetActiveDevice(); |
1737 | |
1738 | === modified file 'tests/autopilot/ubuntu_system_settings/__init__.py' |
1739 | --- tests/autopilot/ubuntu_system_settings/__init__.py 2015-05-28 14:09:07 +0000 |
1740 | +++ tests/autopilot/ubuntu_system_settings/__init__.py 2015-07-01 18:56:14 +0000 |
1741 | @@ -978,15 +978,23 @@ |
1742 | |
1743 | """ |
1744 | @autopilot.logging.log_action(logger.debug) |
1745 | - def connect_to_hidden_network(self, name, security="none", password=None, |
1746 | - cancel=False, scroll_to_and_click=None): |
1747 | - dialog = self._click_connect_to_hidden_network(scroll_to_and_click) |
1748 | + def connect_to_hidden_network(self, name, security='none', username=None, |
1749 | + password=None, auth=None, protocol=None, |
1750 | + cancel=False): |
1751 | + dialog = self._click_connect_to_hidden_network() |
1752 | + dialog._scroll_to_and_click = self._scroll_to_and_click |
1753 | dialog.enter_name(name) |
1754 | + |
1755 | if security: |
1756 | dialog.set_security(security) |
1757 | + if auth: |
1758 | + dialog.set_auth(auth) |
1759 | + if protocol: |
1760 | + dialog.set_protocol(protocol) |
1761 | + if username: |
1762 | + dialog.enter_username(username) |
1763 | if password: |
1764 | dialog.enter_password(password) |
1765 | - |
1766 | if cancel: |
1767 | dialog.cancel() |
1768 | return self |
1769 | @@ -995,7 +1003,7 @@ |
1770 | return dialog |
1771 | |
1772 | @autopilot.logging.log_action(logger.debug) |
1773 | - def _click_connect_to_hidden_network(self, scroll_to_and_click): |
1774 | + def _click_connect_to_hidden_network(self): |
1775 | |
1776 | # we can't mock the qunitymenu items, so we |
1777 | # have to wait for them to be built |
1778 | @@ -1003,16 +1011,13 @@ |
1779 | |
1780 | button = self.select_single('*', |
1781 | objectName='connectToHiddenNetwork') |
1782 | - if (scroll_to_and_click): |
1783 | - scroll_to_and_click(button) |
1784 | - else: |
1785 | - self.pointing_device.click_object(button) |
1786 | + self._scroll_to_and_click(button) |
1787 | return self.get_root_instance().wait_select_single( |
1788 | objectName='otherNetworkDialog') |
1789 | |
1790 | @autopilot.logging.log_action(logger.debug) |
1791 | - def go_to_previous_networks(self, scroll_to_and_click=None): |
1792 | - return self._click_previous_network(scroll_to_and_click) |
1793 | + def go_to_previous_networks(self): |
1794 | + return self._click_previous_network() |
1795 | |
1796 | """Removes previous network |
1797 | |
1798 | @@ -1022,14 +1027,14 @@ |
1799 | |
1800 | """ |
1801 | @autopilot.logging.log_action(logger.debug) |
1802 | - def remove_previous_network(self, ssid, scroll_to_and_click=None): |
1803 | - page = self.go_to_previous_networks(scroll_to_and_click) |
1804 | + def remove_previous_network(self, ssid): |
1805 | + page = self.go_to_previous_networks() |
1806 | details = page.select_network(ssid) |
1807 | details.forget_network() |
1808 | return page |
1809 | |
1810 | @autopilot.logging.log_action(logger.debug) |
1811 | - def _click_previous_network(self, scroll_to_and_click): |
1812 | + def _click_previous_network(self): |
1813 | |
1814 | # we can't mock the qunitymenu items, so we |
1815 | # have to wait for them to be built |
1816 | @@ -1037,10 +1042,7 @@ |
1817 | |
1818 | button = self.select_single('*', |
1819 | objectName='previousNetwork') |
1820 | - if (scroll_to_and_click): |
1821 | - scroll_to_and_click(button) |
1822 | - else: |
1823 | - self.pointing_device.click_object(button) |
1824 | + self._scroll_to_and_click(button) |
1825 | return self.get_root_instance().wait_select_single( |
1826 | objectName='previousNetworksPage') |
1827 | |
1828 | @@ -1058,6 +1060,18 @@ |
1829 | return True |
1830 | return False |
1831 | |
1832 | + # FIXME: Use ListItem methods instead. |
1833 | + @autopilot.logging.log_action(logger.debug) |
1834 | + def _expand_list(self, list_name): |
1835 | + item_list = self.select_single( |
1836 | + '*', objectName=list_name) |
1837 | + active_child = item_list.select_single( |
1838 | + '*', objectName='listContainer') |
1839 | + self._scroll_to_and_click(active_child) |
1840 | + # wait for it to expand |
1841 | + sleep(0.5) |
1842 | + return item_list |
1843 | + |
1844 | @autopilot.logging.log_action(logger.debug) |
1845 | def enter_name(self, name): |
1846 | self._enter_name(name) |
1847 | @@ -1066,16 +1080,27 @@ |
1848 | def _enter_name(self, name): |
1849 | namefield = self.select_single('TextField', |
1850 | objectName='networkname') |
1851 | + self._scroll_to_and_click(namefield) |
1852 | namefield.write(name) |
1853 | |
1854 | @autopilot.logging.log_action(logger.debug) |
1855 | + def enter_username(self, username): |
1856 | + self._enter_username(username) |
1857 | + |
1858 | + @autopilot.logging.log_action(logger.debug) |
1859 | + def _enter_username(self, username): |
1860 | + namefield = self.select_single('TextField', |
1861 | + objectName='username') |
1862 | + self._scroll_to_and_click(namefield) |
1863 | + namefield.write(username) |
1864 | + |
1865 | + @autopilot.logging.log_action(logger.debug) |
1866 | def set_security(self, security): |
1867 | """Sets the hidden network's security |
1868 | |
1869 | - :param security: Either "none", "wpa" or "wep |
1870 | + :param security: Either 'none', 'wpa', 'wep', 'wpa-ep', 'dewp', 'leap' |
1871 | |
1872 | :returns: None |
1873 | - |
1874 | """ |
1875 | |
1876 | # We only set security if not none, since none is default |
1877 | @@ -1083,35 +1108,28 @@ |
1878 | self._set_security(security) |
1879 | |
1880 | @autopilot.logging.log_action(logger.debug) |
1881 | - def _expand_security_list(self): |
1882 | - sec_list = self.select_single( |
1883 | - '*', objectName='securityList') |
1884 | - active_child = sec_list.select_single( |
1885 | - '*', objectName='listContainer') |
1886 | - self.pointing_device.click_object(active_child) |
1887 | - # wait for it to expand |
1888 | - sleep(0.5) |
1889 | - return sec_list |
1890 | - |
1891 | - @autopilot.logging.log_action(logger.debug) |
1892 | def _set_security(self, security): |
1893 | + s_list = self._expand_list('securityList') |
1894 | + item = None |
1895 | if security == 'none': |
1896 | - sec_list = self._expand_security_list() |
1897 | - item = sec_list.wait_select_single('*', |
1898 | - text=_('None')) |
1899 | - self.pointing_device.click_object(item) |
1900 | + item = s_list.wait_select_single('*', text=_('None')) |
1901 | elif security == 'wpa': |
1902 | - sec_list = self._expand_security_list() |
1903 | - item = sec_list.wait_select_single('*', |
1904 | - text=_('WPA & WPA2 Personal')) |
1905 | - self.pointing_device.click_object(item) |
1906 | + item = s_list.wait_select_single('*', |
1907 | + text=_('WPA & WPA2 Personal')) |
1908 | elif security == 'wep': |
1909 | - sec_list = self._expand_security_list() |
1910 | - item = sec_list.wait_select_single('*', |
1911 | - text=_('WEP')) |
1912 | - self.pointing_device.click_object(item) |
1913 | + item = s_list.wait_select_single('*', text=_('WEP')) |
1914 | + elif security == 'wpa-ep': |
1915 | + item = s_list.wait_select_single('*', |
1916 | + text=_('WPA & WPA2 Enterprise')) |
1917 | + elif security == 'dwep': |
1918 | + item = s_list.wait_select_single('*', |
1919 | + text=_('Dynamic WEP (802.1x)')) |
1920 | + elif security == 'leap': |
1921 | + item = s_list.wait_select_single('*', text=_('LEAP')) |
1922 | elif security is not None: |
1923 | raise ValueError('security type %s is not valid' % security) |
1924 | + |
1925 | + self.pointing_device.click_object(item) |
1926 | # wait for ui to change |
1927 | sleep(0.5) |
1928 | |
1929 | @@ -1123,16 +1141,78 @@ |
1930 | def _enter_password(self, password): |
1931 | pwdfield = self.select_single('TextField', |
1932 | objectName='password') |
1933 | + self._scroll_to_and_click(pwdfield) |
1934 | pwdfield.write(password) |
1935 | |
1936 | @autopilot.logging.log_action(logger.debug) |
1937 | + def set_protocol(self, protocol): |
1938 | + """Sets the hidden network's protocol. |
1939 | + |
1940 | + :param protocol: Either 'pap', 'mschapv2', 'mschap', 'chap', 'gtc', |
1941 | + or 'md5'. |
1942 | + """ |
1943 | + self._set_protocol(protocol) |
1944 | + |
1945 | + @autopilot.logging.log_action(logger.debug) |
1946 | + def _set_protocol(self, protocol): |
1947 | + p_list = self._expand_list('p2authList') |
1948 | + item = None |
1949 | + if protocol == 'pap': |
1950 | + item = p_list.wait_select_single(text='PAP') |
1951 | + elif protocol == 'mschapv2': |
1952 | + item = p_list.wait_select_single(text='MSCHAPv2') |
1953 | + elif protocol == 'mschap': |
1954 | + item = p_list.wait_select_single(text='MSCHAP') |
1955 | + elif protocol == 'chap': |
1956 | + item = p_list.wait_select_single(text='CHAP') |
1957 | + elif protocol == 'gtc': |
1958 | + item = p_list.wait_select_single(text='GTC') |
1959 | + elif protocol == 'md5': |
1960 | + item = p_list.wait_select_single(text='MD5') |
1961 | + elif protocol is not None: |
1962 | + raise ValueError('protocol type %s is not valid' % protocol) |
1963 | + |
1964 | + self.pointing_device.click_object(item) |
1965 | + # wait for ui to change |
1966 | + sleep(0.5) |
1967 | + |
1968 | + @autopilot.logging.log_action(logger.debug) |
1969 | + def set_auth(self, auth): |
1970 | + """Sets the hidden network's protocol. |
1971 | + |
1972 | + :param auth: Either 'tls', 'ttls', 'leap', 'fast' or 'peap'. |
1973 | + """ |
1974 | + self._set_auth(auth) |
1975 | + |
1976 | + @autopilot.logging.log_action(logger.debug) |
1977 | + def _set_auth(self, auth): |
1978 | + a_list = self._expand_list('authList') |
1979 | + item = None |
1980 | + if auth == 'tls': |
1981 | + item = a_list.wait_select_single(text='TLS') |
1982 | + elif auth == 'ttls': |
1983 | + item = a_list.wait_select_single(text='TTLS') |
1984 | + elif auth == 'leap': |
1985 | + item = a_list.wait_select_single(text='LEAP') |
1986 | + elif auth == 'fast': |
1987 | + item = a_list.wait_select_single(text='FAST') |
1988 | + elif auth == 'peap': |
1989 | + item = a_list.wait_select_single(text='PEAP') |
1990 | + elif auth is not None: |
1991 | + raise ValueError('auth type %s is not valid' % auth) |
1992 | + |
1993 | + self.pointing_device.click_object(item) |
1994 | + # wait for ui to change |
1995 | + sleep(0.5) |
1996 | + |
1997 | + @autopilot.logging.log_action(logger.debug) |
1998 | def cancel(self): |
1999 | self._click_cancel() |
2000 | |
2001 | @autopilot.logging.log_action(logger.debug) |
2002 | def _click_cancel(self): |
2003 | button = self.select_single('Button', objectName='cancel') |
2004 | - self.pointing_device.click_object(button) |
2005 | + self._scroll_to_and_click(button) |
2006 | |
2007 | @autopilot.logging.log_action(logger.debug) |
2008 | def connect(self): |
2009 | @@ -1141,7 +1221,7 @@ |
2010 | @autopilot.logging.log_action(logger.debug) |
2011 | def _click_connect(self): |
2012 | button = self.select_single('Button', objectName='connect') |
2013 | - self.pointing_device.click_object(button) |
2014 | + self._scroll_to_and_click(button) |
2015 | |
2016 | |
2017 | class PreviousNetworks( |
2018 | |
2019 | === modified file 'tests/autopilot/ubuntu_system_settings/tests/__init__.py' |
2020 | --- tests/autopilot/ubuntu_system_settings/tests/__init__.py 2015-05-15 12:47:14 +0000 |
2021 | +++ tests/autopilot/ubuntu_system_settings/tests/__init__.py 2015-07-01 18:56:14 +0000 |
2022 | @@ -863,9 +863,9 @@ |
2023 | cls.start_system_bus() |
2024 | cls.dbus_con = cls.get_dbus(True) |
2025 | # Add a mock NetworkManager environment so we get consistent results |
2026 | + template = os.path.join(os.path.dirname(__file__), 'networkmanager.py') |
2027 | (cls.p_mock, cls.obj_nm) = cls.spawn_server_template( |
2028 | - 'networkmanager', stdout=subprocess.PIPE) |
2029 | - cls.dbusmock = dbus.Interface(cls.obj_nm, dbusmock.MOCK_IFACE) |
2030 | + template, stdout=subprocess.PIPE) |
2031 | |
2032 | def setUp(self, panel=None): |
2033 | self.obj_nm.Reset() |
2034 | @@ -874,19 +874,28 @@ |
2035 | NM_SERVICE, self.device_path), |
2036 | dbusmock.MOCK_IFACE) |
2037 | |
2038 | - self.ap_mock = self.create_access_point('test_ap', 'test_ap') |
2039 | + self.ap_mock = self.create_access_point( |
2040 | + 'test_ap', 'test_ap', |
2041 | + security=NM80211ApSecurityFlags.NM_802_11_AP_SEC_KEY_MGMT_PSK |
2042 | + ) |
2043 | |
2044 | super(WifiBaseTestCase, self).setUp() |
2045 | self.wifi_page = self.main_view.go_to_wifi_page() |
2046 | |
2047 | - def create_access_point(self, name, ssid, secured=True): |
2048 | - |
2049 | - if secured: |
2050 | - security = NM80211ApSecurityFlags.NM_802_11_AP_SEC_KEY_MGMT_PSK |
2051 | - else: |
2052 | + def create_access_point(self, name, ssid, security=None): |
2053 | + """Creates access point. |
2054 | + |
2055 | + :param name: Name of access point |
2056 | + :param ssid: SSID of access point |
2057 | + :param security: Either None, or a NM80211ApSecurityFlags |
2058 | + |
2059 | + :returns: Access point |
2060 | + |
2061 | + """ |
2062 | + if security is None: |
2063 | security = NM80211ApSecurityFlags.NM_802_11_AP_SEC_NONE |
2064 | |
2065 | - return self.dbusmock.AddAccessPoint( |
2066 | + return self.obj_nm.AddAccessPoint( |
2067 | self.device_path, name, ssid, self.random_mac_address(), |
2068 | InfrastructureMode.NM_802_11_MODE_INFRA, 2425, 5400, 82, security) |
2069 | |
2070 | |
2071 | === added file 'tests/autopilot/ubuntu_system_settings/tests/networkmanager.py' |
2072 | --- tests/autopilot/ubuntu_system_settings/tests/networkmanager.py 1970-01-01 00:00:00 +0000 |
2073 | +++ tests/autopilot/ubuntu_system_settings/tests/networkmanager.py 2015-07-01 18:56:14 +0000 |
2074 | @@ -0,0 +1,960 @@ |
2075 | +'''NetworkManager mock template |
2076 | + |
2077 | +This creates the expected methods and properties of the main |
2078 | +org.freedesktop.NetworkManager object, but no devices. You can specify any |
2079 | +property such as 'NetworkingEnabled', or 'WirelessEnabled' etc. in |
2080 | +"parameters". |
2081 | +''' |
2082 | + |
2083 | +# This program is free software; you can redistribute it and/or modify it under |
2084 | +# the terms of the GNU Lesser General Public License as published by the Free |
2085 | +# Software Foundation; either version 3 of the License, or (at your option) any |
2086 | +# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text |
2087 | +# of the license. |
2088 | + |
2089 | +__author__ = 'Iftikhar Ahmad' |
2090 | +__email__ = 'iftikhar.ahmad@canonical.com' |
2091 | +__copyright__ = '(c) 2012 Canonical Ltd.' |
2092 | +__license__ = 'LGPL 3+' |
2093 | + |
2094 | +import dbus |
2095 | +import uuid |
2096 | +import binascii |
2097 | + |
2098 | +from dbusmock import MOCK_IFACE |
2099 | +import dbusmock |
2100 | + |
2101 | + |
2102 | +BUS_NAME = 'org.freedesktop.NetworkManager' |
2103 | +MAIN_OBJ = '/org/freedesktop/NetworkManager' |
2104 | +MAIN_IFACE = 'org.freedesktop.NetworkManager' |
2105 | +SETTINGS_OBJ = '/org/freedesktop/NetworkManager/Settings' |
2106 | +SETTINGS_IFACE = 'org.freedesktop.NetworkManager.Settings' |
2107 | +DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device' |
2108 | +WIRELESS_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless' |
2109 | +ACCESS_POINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint' |
2110 | +CSETTINGS_IFACE = 'org.freedesktop.NetworkManager.Settings.Connection' |
2111 | +ACTIVE_CONNECTION_IFACE = 'org.freedesktop.NetworkManager.Connection.Active' |
2112 | +ACTIVE_CONNECTION_PATH = '/org/freedesktop/NetworkManager/ActiveConnection/' |
2113 | +SYSTEM_BUS = True |
2114 | + |
2115 | + |
2116 | +class NMState: |
2117 | + '''Global state |
2118 | + |
2119 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html |
2120 | + #type-NM_STATE |
2121 | + ''' |
2122 | + NM_STATE_UNKNOWN = 0 |
2123 | + NM_STATE_ASLEEP = 10 |
2124 | + NM_STATE_DISCONNECTED = 20 |
2125 | + NM_STATE_DISCONNECTING = 30 |
2126 | + NM_STATE_CONNECTING = 40 |
2127 | + NM_STATE_CONNECTED_LOCAL = 50 |
2128 | + NM_STATE_CONNECTED_SITE = 60 |
2129 | + NM_STATE_CONNECTED_GLOBAL = 70 |
2130 | + |
2131 | + |
2132 | +class NMConnectivityState: |
2133 | + '''Connectvity state |
2134 | + |
2135 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html |
2136 | + #type-NM_CONNECTIVITY |
2137 | + ''' |
2138 | + NM_CONNECTIVITY_UNKNOWN = 0 |
2139 | + NM_CONNECTIVITY_NONE = 1 |
2140 | + NM_CONNECTIVITY_PORTAL = 2 |
2141 | + NM_CONNECTIVITY_LIMITED = 3 |
2142 | + NM_CONNECTIVITY_FULL = 4 |
2143 | + |
2144 | + |
2145 | +class NMActiveConnectionState: |
2146 | + '''Active connection state |
2147 | + |
2148 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html |
2149 | + #type-NM_ACTIVE_CONNECTION_STATE |
2150 | + ''' |
2151 | + NM_ACTIVE_CONNECTION_STATE_UNKNOWN = 0 |
2152 | + NM_ACTIVE_CONNECTION_STATE_ACTIVATING = 1 |
2153 | + NM_ACTIVE_CONNECTION_STATE_ACTIVATED = 2 |
2154 | + NM_ACTIVE_CONNECTION_STATE_DEACTIVATING = 3 |
2155 | + NM_ACTIVE_CONNECTION_STATE_DEACTIVATED = 4 |
2156 | + |
2157 | + |
2158 | +class InfrastructureMode: |
2159 | + '''Infrastructure mode |
2160 | + |
2161 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html |
2162 | + #type-NM_802_11_MODE |
2163 | + ''' |
2164 | + NM_802_11_MODE_UNKNOWN = 0 |
2165 | + NM_802_11_MODE_ADHOC = 1 |
2166 | + NM_802_11_MODE_INFRA = 2 |
2167 | + NM_802_11_MODE_AP = 3 |
2168 | + |
2169 | + NAME_MAP = { |
2170 | + NM_802_11_MODE_UNKNOWN: 'unknown', |
2171 | + NM_802_11_MODE_ADHOC: 'adhoc', |
2172 | + NM_802_11_MODE_INFRA: 'infrastructure', |
2173 | + NM_802_11_MODE_AP: 'access-point', |
2174 | + } |
2175 | + |
2176 | + |
2177 | +class DeviceState: |
2178 | + '''Device states |
2179 | + |
2180 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html |
2181 | + #type-NM_DEVICE_STATE |
2182 | + ''' |
2183 | + UNKNOWN = 0 |
2184 | + UNMANAGED = 10 |
2185 | + UNAVAILABLE = 20 |
2186 | + DISCONNECTED = 30 |
2187 | + PREPARE = 40 |
2188 | + CONFIG = 50 |
2189 | + NEED_AUTH = 60 |
2190 | + IP_CONFIG = 70 |
2191 | + IP_CHECK = 80 |
2192 | + SECONDARIES = 90 |
2193 | + ACTIVATED = 100 |
2194 | + DEACTIVATING = 110 |
2195 | + FAILED = 120 |
2196 | + |
2197 | + |
2198 | +class NM80211ApSecurityFlags: |
2199 | + '''Security flags |
2200 | + |
2201 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html |
2202 | + #type-NM_802_11_AP_SEC |
2203 | + ''' |
2204 | + NM_802_11_AP_SEC_NONE = 0x00000000 |
2205 | + NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001 |
2206 | + NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002 |
2207 | + NM_802_11_AP_SEC_PAIR_TKIP = 0x00000004 |
2208 | + NM_802_11_AP_SEC_PAIR_CCMP = 0x00000008 |
2209 | + NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010 |
2210 | + NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020 |
2211 | + NM_802_11_AP_SEC_GROUP_TKIP = 0x00000040 |
2212 | + NM_802_11_AP_SEC_GROUP_CCMP = 0x00000080 |
2213 | + NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100 |
2214 | + NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200 |
2215 | + |
2216 | + NAME_MAP = { |
2217 | + NM_802_11_AP_SEC_KEY_MGMT_PSK: { |
2218 | + 'key-mgmt': 'wpa-psk', |
2219 | + 'auth-alg': 'open' |
2220 | + }, |
2221 | + NM_802_11_AP_SEC_KEY_MGMT_802_1X: { |
2222 | + 'key-mgmt': 'wpa-eap' |
2223 | + }, |
2224 | + } |
2225 | + |
2226 | + |
2227 | +class NM80211ApFlags: |
2228 | + '''Device flags |
2229 | + |
2230 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html |
2231 | + #type-NM_802_11_AP_FLAGS |
2232 | + ''' |
2233 | + NM_802_11_AP_FLAGS_NONE = 0x00000000 |
2234 | + NM_802_11_AP_FLAGS_PRIVACY = 0x00000001 |
2235 | + |
2236 | + |
2237 | +def activate_connection(self, conn, dev, ap): |
2238 | + name = ap.rsplit('/', 1)[1] |
2239 | + RemoveActiveConnection(self, dev, ACTIVE_CONNECTION_PATH + name) |
2240 | + |
2241 | + state = dbus.UInt32( |
2242 | + NMActiveConnectionState.NM_ACTIVE_CONNECTION_STATE_ACTIVATED |
2243 | + ) |
2244 | + active_conn = dbus.ObjectPath( |
2245 | + AddActiveConnection(self, [dev], conn, ap, name, state) |
2246 | + ) |
2247 | + |
2248 | + return active_conn |
2249 | + |
2250 | + |
2251 | +def deactivate_connection(self, active_conn_path): |
2252 | + NM = dbusmock.get_object(MAIN_OBJ) |
2253 | + |
2254 | + for dev_path in NM.GetDevices(): |
2255 | + RemoveActiveConnection(self, dev_path, active_conn_path) |
2256 | + |
2257 | + |
2258 | +def add_and_activate_connection(self, conn_conf, dev, ap): |
2259 | + name = ap.rsplit('/', 1)[1] |
2260 | + RemoveWifiConnection( |
2261 | + self, dev, '/org/freedesktop/NetworkManager/Settings/' + name |
2262 | + ) |
2263 | + |
2264 | + raw_ssid = ''.join( |
2265 | + [chr(byte) for byte in conn_conf["802-11-wireless"]["ssid"]] |
2266 | + ) |
2267 | + wifi_conn = dbus.ObjectPath( |
2268 | + AddWiFiConnection(self, dev, name, raw_ssid, "", conn_conf) |
2269 | + ) |
2270 | + |
2271 | + active_conn = activate_connection(self, wifi_conn, dev, ap) |
2272 | + |
2273 | + return (wifi_conn, active_conn) |
2274 | + |
2275 | + |
2276 | +def load(mock, parameters): |
2277 | + mock.activate_connection = activate_connection |
2278 | + mock.deactivate_connection = deactivate_connection |
2279 | + mock.add_and_activate_connection = add_and_activate_connection |
2280 | + mock.AddMethods(MAIN_IFACE, [ |
2281 | + ( |
2282 | + 'GetDevices', '', 'ao', |
2283 | + 'ret = [k for k in objects.keys() if "/Devices" in k]' |
2284 | + ), |
2285 | + ('GetPermissions', '', 'a{ss}', 'ret = {}'), |
2286 | + ('state', '', 'u', "ret = self.Get('%s', 'State')" % MAIN_IFACE), |
2287 | + ( |
2288 | + 'CheckConnectivity', '', 'u', |
2289 | + "ret = self.Get('%s', 'Connectivity')" % MAIN_IFACE |
2290 | + ), |
2291 | + ( |
2292 | + 'ActivateConnection', 'ooo', 'o', |
2293 | + "ret = self.activate_connection(self, args[0], args[1], args[2])" |
2294 | + ), |
2295 | + ( |
2296 | + 'DeactivateConnection', 'o', '', |
2297 | + "self.deactivate_connection(self, args[0])" |
2298 | + ), |
2299 | + ( |
2300 | + 'AddAndActivateConnection', 'a{sa{sv}}oo', 'oo', |
2301 | + "ret = self.add_and_activate_connection(" + |
2302 | + "self, args[0], args[1], args[2])" |
2303 | + ), |
2304 | + ]) |
2305 | + |
2306 | + mock.AddProperties( |
2307 | + '', |
2308 | + { |
2309 | + 'ActiveConnections': dbus.Array([], signature='o'), |
2310 | + 'Devices': dbus.Array([], signature='o'), |
2311 | + 'NetworkingEnabled': parameters.get('NetworkingEnabled', True), |
2312 | + 'Connectivity': parameters.get( |
2313 | + 'Connectivity', |
2314 | + dbus.UInt32(NMConnectivityState.NM_CONNECTIVITY_FULL) |
2315 | + ), |
2316 | + 'State': parameters.get( |
2317 | + 'State', dbus.UInt32(NMState.NM_STATE_CONNECTED_GLOBAL) |
2318 | + ), |
2319 | + 'Startup': False, |
2320 | + 'Version': parameters.get('Version', '0.9.6.0'), |
2321 | + 'WimaxEnabled': parameters.get('WimaxEnabled', True), |
2322 | + 'WimaxHardwareEnabled': parameters.get( |
2323 | + 'WimaxHardwareEnabled', True |
2324 | + ), |
2325 | + 'WirelessEnabled': parameters.get('WirelessEnabled', True), |
2326 | + 'WirelessHardwareEnabled': parameters.get( |
2327 | + 'WirelessHardwareEnabled', True |
2328 | + ), |
2329 | + 'WwanEnabled': parameters.get('WwanEnabled', False), |
2330 | + 'WwanHardwareEnabled': parameters.get('WwanHardwareEnabled', True) |
2331 | + } |
2332 | + ) |
2333 | + |
2334 | + settings_props = {'Hostname': 'hostname', |
2335 | + 'CanModify': True, |
2336 | + 'Connections': dbus.Array([], signature='o')} |
2337 | + settings_methods = [ |
2338 | + ( |
2339 | + 'ListConnections', '', 'ao', |
2340 | + "ret = self.Get('%s', 'Connections')" % SETTINGS_IFACE |
2341 | + ), |
2342 | + ('GetConnectionByUuid', 's', 'o', ''), |
2343 | + ( |
2344 | + 'AddConnection', 'a{sa{sv}}', 'o', |
2345 | + 'ret = self.SettingsAddConnection(args[0])' |
2346 | + ), |
2347 | + ('SaveHostname', 's', '', '') |
2348 | + ] |
2349 | + mock.AddObject(SETTINGS_OBJ, |
2350 | + SETTINGS_IFACE, |
2351 | + settings_props, |
2352 | + settings_methods) |
2353 | + |
2354 | + |
2355 | +@dbus.service.method(MOCK_IFACE, |
2356 | + in_signature='sssv', out_signature='') |
2357 | +def SetProperty(self, path, iface, name, value): |
2358 | + obj = dbusmock.get_object(path) |
2359 | + obj.Set(iface, name, value) |
2360 | + obj.EmitSignal(iface, 'PropertiesChanged', 'a{sv}', [{name: value}]) |
2361 | + |
2362 | + |
2363 | +@dbus.service.method(MOCK_IFACE, |
2364 | + in_signature='u', out_signature='') |
2365 | +def SetGlobalConnectionState(self, state): |
2366 | + self.SetProperty( |
2367 | + MAIN_OBJ, MAIN_IFACE, 'State', |
2368 | + dbus.UInt32(state, variant_level=1) |
2369 | + ) |
2370 | + self.EmitSignal(MAIN_IFACE, 'StateChanged', 'u', [state]) |
2371 | + |
2372 | + |
2373 | +@dbus.service.method(MOCK_IFACE, |
2374 | + in_signature='u', out_signature='') |
2375 | +def SetConnectivity(self, connectivity): |
2376 | + self.SetProperty( |
2377 | + MAIN_OBJ, MAIN_IFACE, 'Connectivity', |
2378 | + dbus.UInt32(connectivity, variant_level=1) |
2379 | + ) |
2380 | + |
2381 | + |
2382 | +@dbus.service.method(MOCK_IFACE, |
2383 | + in_signature='ss', out_signature='') |
2384 | +def SetDeviceActive(self, device_path, active_connection_path): |
2385 | + dev_obj = dbusmock.get_object(device_path) |
2386 | + dev_obj.Set( |
2387 | + DEVICE_IFACE, 'ActiveConnection', |
2388 | + dbus.ObjectPath(active_connection_path) |
2389 | + ) |
2390 | + old_state = dev_obj.Get(DEVICE_IFACE, 'State') |
2391 | + dev_obj.Set(DEVICE_IFACE, 'State', dbus.UInt32(DeviceState.ACTIVATED)) |
2392 | + |
2393 | + dev_obj.EmitSignal( |
2394 | + DEVICE_IFACE, 'StateChanged', 'uuu', |
2395 | + [ |
2396 | + dbus.UInt32(DeviceState.ACTIVATED), |
2397 | + old_state, |
2398 | + dbus.UInt32(1) |
2399 | + ] |
2400 | + ) |
2401 | + |
2402 | + |
2403 | +@dbus.service.method(MOCK_IFACE, |
2404 | + in_signature='s', out_signature='') |
2405 | +def SetDeviceDisconnected(self, device_path): |
2406 | + dev_obj = dbusmock.get_object(device_path) |
2407 | + dev_obj.Set(DEVICE_IFACE, 'ActiveConnection', dbus.ObjectPath('/')) |
2408 | + old_state = dev_obj.Get(DEVICE_IFACE, 'State') |
2409 | + dev_obj.Set(DEVICE_IFACE, 'State', dbus.UInt32(DeviceState.DISCONNECTED)) |
2410 | + |
2411 | + dev_obj.EmitSignal( |
2412 | + DEVICE_IFACE, 'StateChanged', 'uuu', |
2413 | + [ |
2414 | + dbus.UInt32(DeviceState.DISCONNECTED), |
2415 | + old_state, |
2416 | + dbus.UInt32(1) |
2417 | + ] |
2418 | + ) |
2419 | + |
2420 | + |
2421 | +@dbus.service.method(MOCK_IFACE, |
2422 | + in_signature='ssi', out_signature='s') |
2423 | +def AddEthernetDevice(self, device_name, iface_name, state): |
2424 | + '''Add an ethernet device. |
2425 | + |
2426 | + You have to specify device_name, device interface name (e. g. eth0), and |
2427 | + state. You can use the predefined DeviceState values (e. g. |
2428 | + DeviceState.ACTIVATED) or supply a numeric value. For valid state values |
2429 | + please visit |
2430 | + http://projects.gnome.org/NetworkManager/developers/api/09/spec.html |
2431 | + #type-NM_DEVICE_STATE |
2432 | + |
2433 | + Please note that this does not set any global properties. |
2434 | + |
2435 | + Returns the new object path. |
2436 | + ''' |
2437 | + path = '/org/freedesktop/NetworkManager/Devices/' + device_name |
2438 | + wired_props = {'Carrier': False, |
2439 | + 'HwAddress': dbus.String('78:DD:08:D2:3D:43'), |
2440 | + 'PermHwAddress': dbus.String('78:DD:08:D2:3D:43'), |
2441 | + 'Speed': dbus.UInt32(0)} |
2442 | + self.AddObject(path, |
2443 | + 'org.freedesktop.NetworkManager.Device.Wired', |
2444 | + wired_props, |
2445 | + []) |
2446 | + |
2447 | + props = {'DeviceType': dbus.UInt32(1), |
2448 | + 'State': dbus.UInt32(state), |
2449 | + 'Interface': iface_name, |
2450 | + 'AvailableConnections': dbus.Array([], signature='o'), |
2451 | + 'IpInterface': ''} |
2452 | + |
2453 | + obj = dbusmock.get_object(path) |
2454 | + obj.AddProperties(DEVICE_IFACE, props) |
2455 | + |
2456 | + devices = self.Get(MAIN_IFACE, 'Devices') |
2457 | + devices.append(path) |
2458 | + self.Set(MAIN_IFACE, 'Devices', devices) |
2459 | + |
2460 | + self.EmitSignal( |
2461 | + 'org.freedesktop.NetworkManager', 'DeviceAdded', 'o', [path] |
2462 | + ) |
2463 | + |
2464 | + return path |
2465 | + |
2466 | + |
2467 | +@dbus.service.method(MOCK_IFACE, |
2468 | + in_signature='ssi', out_signature='s') |
2469 | +def AddWiFiDevice(self, device_name, iface_name, state): |
2470 | + '''Add a WiFi Device. |
2471 | + |
2472 | + You have to specify device_name, device interface name (e. g. wlan0) and |
2473 | + state. You can use the predefined DeviceState values (e. g. |
2474 | + DeviceState.ACTIVATED) or supply a numeric value. For valid state values, |
2475 | + please visit |
2476 | + http://projects.gnome.org/NetworkManager/developers/api/09/spec.html |
2477 | + #type-NM_DEVICE_STATE |
2478 | + |
2479 | + Please note that this does not set any global properties. |
2480 | + |
2481 | + Returns the new object path. |
2482 | + ''' |
2483 | + |
2484 | + path = '/org/freedesktop/NetworkManager/Devices/' + device_name |
2485 | + self.AddObject(path, |
2486 | + WIRELESS_DEVICE_IFACE, |
2487 | + { |
2488 | + 'HwAddress': dbus.String('11:22:33:44:55:66'), |
2489 | + 'PermHwAddress': dbus.String('11:22:33:44:55:66'), |
2490 | + 'Bitrate': dbus.UInt32(5400), |
2491 | + 'Mode': dbus.UInt32(2), |
2492 | + 'WirelessCapabilities': dbus.UInt32(255), |
2493 | + 'AccessPoints': dbus.Array([], signature='o'), |
2494 | + }, |
2495 | + [ |
2496 | + ('GetAccessPoints', '', 'ao', |
2497 | + 'ret = self.access_points'), |
2498 | + ('GetAllAccessPoints', '', 'ao', |
2499 | + 'ret = self.access_points'), |
2500 | + ('RequestScan', 'a{sv}', '', ''), |
2501 | + ]) |
2502 | + |
2503 | + dev_obj = dbusmock.get_object(path) |
2504 | + dev_obj.access_points = [] |
2505 | + dev_obj.AddProperties( |
2506 | + DEVICE_IFACE, |
2507 | + { |
2508 | + 'ActiveConnection': dbus.ObjectPath('/'), |
2509 | + 'AvailableConnections': dbus.Array([], signature='o'), |
2510 | + 'AutoConnect': False, |
2511 | + 'Managed': True, |
2512 | + 'Driver': 'dbusmock', |
2513 | + 'DeviceType': dbus.UInt32(2), |
2514 | + 'State': dbus.UInt32(state), |
2515 | + 'Interface': iface_name, |
2516 | + 'IpInterface': iface_name, |
2517 | + } |
2518 | + ) |
2519 | + |
2520 | + devices = self.Get(MAIN_IFACE, 'Devices') |
2521 | + devices.append(path) |
2522 | + self.Set(MAIN_IFACE, 'Devices', devices) |
2523 | + |
2524 | + self.EmitSignal( |
2525 | + 'org.freedesktop.NetworkManager', 'DeviceAdded', 'o', [path] |
2526 | + ) |
2527 | + |
2528 | + return path |
2529 | + |
2530 | + |
2531 | +@dbus.service.method(MOCK_IFACE, |
2532 | + in_signature='ssssuuuyu', out_signature='s') |
2533 | +def AddAccessPoint(self, dev_path, ap_name, ssid, hw_address, |
2534 | + mode, frequency, rate, strength, security): |
2535 | + '''Add an access point to an existing WiFi device. |
2536 | + |
2537 | + You have to specify WiFi Device path, Access Point object name, |
2538 | + ssid, hw_address, mode, frequency, rate, strength and security. |
2539 | + For valid access point property values, please visit |
2540 | + http://projects.gnome.org/NetworkManager/developers/api/09/spec.html# |
2541 | + org.freedesktop.NetworkManager.AccessPoint |
2542 | + |
2543 | + Please note that this does not set any global properties. |
2544 | + |
2545 | + Returns the new object path. |
2546 | + ''' |
2547 | + dev_obj = dbusmock.get_object(dev_path) |
2548 | + ap_path = '/org/freedesktop/NetworkManager/AccessPoint/' + ap_name |
2549 | + if ap_path in dev_obj.access_points: |
2550 | + raise dbus.exceptions.DBusException( |
2551 | + 'Access point %s on device %s already exists' % (ap_name, |
2552 | + dev_path), |
2553 | + name=MAIN_IFACE + '.AlreadyExists') |
2554 | + |
2555 | + flags = NM80211ApFlags.NM_802_11_AP_FLAGS_PRIVACY |
2556 | + if security == NM80211ApSecurityFlags.NM_802_11_AP_SEC_NONE: |
2557 | + flags = NM80211ApFlags.NM_802_11_AP_FLAGS_NONE |
2558 | + |
2559 | + self.AddObject(ap_path, |
2560 | + ACCESS_POINT_IFACE, |
2561 | + {'Ssid': dbus.ByteArray(ssid.encode('UTF-8')), |
2562 | + 'HwAddress': dbus.String(hw_address), |
2563 | + 'Flags': dbus.UInt32(flags), |
2564 | + 'LastSeen': dbus.Int32(1), |
2565 | + 'Frequency': dbus.UInt32(frequency), |
2566 | + 'MaxBitrate': dbus.UInt32(rate), |
2567 | + 'Mode': dbus.UInt32(mode), |
2568 | + 'RsnFlags': dbus.UInt32(security), |
2569 | + 'WpaFlags': dbus.UInt32(security), |
2570 | + 'Strength': dbus.Byte(strength)}, |
2571 | + []) |
2572 | + |
2573 | + dev_obj.access_points.append(ap_path) |
2574 | + |
2575 | + aps = dev_obj.Get(WIRELESS_DEVICE_IFACE, 'AccessPoints') |
2576 | + aps.append(ap_path) |
2577 | + dev_obj.Set(WIRELESS_DEVICE_IFACE, 'AccessPoints', aps) |
2578 | + |
2579 | + dev_obj.EmitSignal( |
2580 | + WIRELESS_DEVICE_IFACE, 'AccessPointAdded', 'o', [ap_path] |
2581 | + ) |
2582 | + |
2583 | + return ap_path |
2584 | + |
2585 | + |
2586 | +@dbus.service.method(MOCK_IFACE, |
2587 | + in_signature='ssssa{sa{sv}}', out_signature='s') |
2588 | +def AddWiFiConnection(self, dev_path, connection_name, ssid_name, key_mgmt, |
2589 | + config): |
2590 | + '''Add an available connection to an existing WiFi device and access point. |
2591 | + |
2592 | + You have to specify WiFi Device path, Connection object name, |
2593 | + SSID and key management. |
2594 | + |
2595 | + The SSID must match one of the previously created access points. |
2596 | + |
2597 | + Please note that this does not set any global properties. |
2598 | + |
2599 | + Returns the new object path. |
2600 | + ''' |
2601 | + |
2602 | + dev_obj = dbusmock.get_object(dev_path) |
2603 | + connection_path = '%s/%s' % (SETTINGS_OBJ, connection_name) |
2604 | + connections = dev_obj.Get(DEVICE_IFACE, 'AvailableConnections') |
2605 | + |
2606 | + settings_obj = dbusmock.get_object(SETTINGS_OBJ) |
2607 | + main_connections = settings_obj.ListConnections() |
2608 | + |
2609 | + ssid = ssid_name.encode('UTF-8') |
2610 | + |
2611 | + # Find the access point by ssid |
2612 | + access_point = None |
2613 | + access_points = dev_obj.access_points |
2614 | + for ap_path in access_points: |
2615 | + ap = dbusmock.get_object(ap_path) |
2616 | + if ap.Get(ACCESS_POINT_IFACE, 'Ssid') == ssid: |
2617 | + access_point = ap |
2618 | + break |
2619 | + |
2620 | + if not access_point: |
2621 | + raise dbus.exceptions.DBusException( |
2622 | + 'Access point with SSID [%s] could not be found' % (ssid_name), |
2623 | + name=MAIN_IFACE + '.DoesNotExist') |
2624 | + |
2625 | + hw_address = access_point.Get(ACCESS_POINT_IFACE, 'HwAddress') |
2626 | + mode = access_point.Get(ACCESS_POINT_IFACE, 'Mode') |
2627 | + security = access_point.Get(ACCESS_POINT_IFACE, 'WpaFlags') |
2628 | + |
2629 | + if connection_path in connections or connection_path in main_connections: |
2630 | + raise dbus.exceptions.DBusException( |
2631 | + 'Connection %s on device %s already exists' % ( |
2632 | + connection_name, dev_path |
2633 | + ), |
2634 | + name=MAIN_IFACE + '.AlreadyExists') |
2635 | + |
2636 | + # Parse mac address string into byte array |
2637 | + mac_bytes = binascii.unhexlify(hw_address.replace(':', '')) |
2638 | + |
2639 | + settings = { |
2640 | + '802-11-wireless': { |
2641 | + 'seen-bssids': [hw_address], |
2642 | + 'ssid': dbus.ByteArray(ssid), |
2643 | + 'mac-address': dbus.ByteArray(mac_bytes), |
2644 | + 'mode': InfrastructureMode.NAME_MAP[mode] |
2645 | + }, |
2646 | + 'connection': { |
2647 | + 'timestamp': dbus.UInt64(1374828522), |
2648 | + 'type': '802-11-wireless', |
2649 | + 'id': ssid_name, |
2650 | + 'uuid': str(uuid.uuid4()) |
2651 | + }, |
2652 | + } |
2653 | + |
2654 | + if security != NM80211ApSecurityFlags.NM_802_11_AP_SEC_NONE: |
2655 | + settings['802-11-wireless']['security'] = '802-11-wireless-security' |
2656 | + settings['802-11-wireless-security'] = ( |
2657 | + NM80211ApSecurityFlags.NAME_MAP[security]) |
2658 | + |
2659 | + if security == NM80211ApSecurityFlags.NM_802_11_AP_SEC_KEY_MGMT_802_1X: |
2660 | + settings['802-1x'] = config['802-1x'] |
2661 | + |
2662 | + self.AddObject( |
2663 | + connection_path, |
2664 | + CSETTINGS_IFACE, |
2665 | + { |
2666 | + 'Settings': dbus.Dictionary(settings, signature='sa{sv}'), |
2667 | + 'Secrets': dbus.Dictionary({}, signature='sa{sv}'), |
2668 | + }, |
2669 | + [ |
2670 | + ( |
2671 | + 'Delete', '', '', |
2672 | + 'self.ConnectionDelete("%s")' % connection_path |
2673 | + ), |
2674 | + ( |
2675 | + 'GetSettings', '', 'a{sa{sv}}', |
2676 | + "ret = self.Get('%s', 'Settings')" % CSETTINGS_IFACE |
2677 | + ), |
2678 | + ( |
2679 | + 'GetSecrets', 's', 'a{sa{sv}}', |
2680 | + "ret = self.Get('%s', 'Secrets')" % CSETTINGS_IFACE |
2681 | + ), |
2682 | + ( |
2683 | + 'Update', 'a{sa{sv}}', '', |
2684 | + 'self.ConnectionUpdate("%s", args[0])' % connection_path |
2685 | + ), |
2686 | + ] |
2687 | + ) |
2688 | + |
2689 | + connections.append(dbus.ObjectPath(connection_path)) |
2690 | + dev_obj.Set(DEVICE_IFACE, 'AvailableConnections', connections) |
2691 | + |
2692 | + main_connections.append(connection_path) |
2693 | + settings_obj.Set(SETTINGS_IFACE, 'Connections', main_connections) |
2694 | + |
2695 | + settings_obj.EmitSignal(SETTINGS_IFACE, 'NewConnection', 'o', [ap_path]) |
2696 | + |
2697 | + return connection_path |
2698 | + |
2699 | + |
2700 | +@dbus.service.method(MOCK_IFACE, |
2701 | + in_signature='assssu', out_signature='s') |
2702 | +def AddActiveConnection(self, devices, connection_device, specific_object, |
2703 | + name, state): |
2704 | + '''Add an active connection to an existing WiFi device. |
2705 | + |
2706 | + You have to a list of the involved WiFi devices, the connection path, |
2707 | + the access point path, ActiveConnection object name and connection |
2708 | + state. |
2709 | + |
2710 | + Please note that this does not set any global properties. |
2711 | + |
2712 | + Returns the new object path. |
2713 | + ''' |
2714 | + |
2715 | + conn_obj = dbusmock.get_object(connection_device) |
2716 | + settings = conn_obj.Get(CSETTINGS_IFACE, 'Settings') |
2717 | + conn_uuid = settings['connection']['uuid'] |
2718 | + |
2719 | + device_objects = [dbus.ObjectPath(dev) for dev in devices] |
2720 | + |
2721 | + active_connection_path = ACTIVE_CONNECTION_PATH + name |
2722 | + self.AddObject(active_connection_path, |
2723 | + ACTIVE_CONNECTION_IFACE, |
2724 | + { |
2725 | + 'Devices': device_objects, |
2726 | + 'Default6': False, |
2727 | + 'Default': True, |
2728 | + 'Vpn': False, |
2729 | + 'Connection': dbus.ObjectPath(connection_device), |
2730 | + 'Master': dbus.ObjectPath('/'), |
2731 | + 'SpecificObject': dbus.ObjectPath(specific_object), |
2732 | + 'Uuid': conn_uuid, |
2733 | + 'State': state, |
2734 | + }, |
2735 | + []) |
2736 | + |
2737 | + for dev_path in devices: |
2738 | + self.SetDeviceActive(dev_path, active_connection_path) |
2739 | + |
2740 | + active_connections = self.Get(MAIN_IFACE, 'ActiveConnections') |
2741 | + active_connections.append(dbus.ObjectPath(active_connection_path)) |
2742 | + self.SetProperty( |
2743 | + MAIN_OBJ, MAIN_IFACE, 'ActiveConnections', active_connections |
2744 | + ) |
2745 | + |
2746 | + return active_connection_path |
2747 | + |
2748 | + |
2749 | +@dbus.service.method(MOCK_IFACE, |
2750 | + in_signature='ss', out_signature='') |
2751 | +def RemoveAccessPoint(self, dev_path, ap_path): |
2752 | + '''Remove the specified access point. |
2753 | + |
2754 | + You have to specify the device to remove the access point from, and the |
2755 | + path of the access point. |
2756 | + |
2757 | + Please note that this does not set any global properties. |
2758 | + ''' |
2759 | + |
2760 | + dev_obj = dbusmock.get_object(dev_path) |
2761 | + |
2762 | + aps = dev_obj.Get(WIRELESS_DEVICE_IFACE, 'AccessPoints') |
2763 | + aps.remove(ap_path) |
2764 | + dev_obj.Set(WIRELESS_DEVICE_IFACE, 'AccessPoints', aps) |
2765 | + |
2766 | + dev_obj.access_points.remove(ap_path) |
2767 | + |
2768 | + dev_obj.EmitSignal( |
2769 | + WIRELESS_DEVICE_IFACE, 'AccessPointRemoved', 'o', [ap_path] |
2770 | + ) |
2771 | + |
2772 | + self.RemoveObject(ap_path) |
2773 | + |
2774 | + |
2775 | +@dbus.service.method(MOCK_IFACE, |
2776 | + in_signature='ss', out_signature='') |
2777 | +def RemoveWifiConnection(self, dev_path, connection_path): |
2778 | + '''Remove the specified WiFi connection. |
2779 | + |
2780 | + You have to specify the device to remove the connection from, and the |
2781 | + path of the Connection. |
2782 | + |
2783 | + Please note that this does not set any global properties. |
2784 | + ''' |
2785 | + |
2786 | + dev_obj = dbusmock.get_object(dev_path) |
2787 | + settings_obj = dbusmock.get_object(SETTINGS_OBJ) |
2788 | + |
2789 | + connections = dev_obj.Get(DEVICE_IFACE, 'AvailableConnections') |
2790 | + main_connections = settings_obj.ListConnections() |
2791 | + |
2792 | + if (connection_path not in connections and |
2793 | + connection_path not in main_connections): |
2794 | + return |
2795 | + |
2796 | + connections.remove(dbus.ObjectPath(connection_path)) |
2797 | + dev_obj.Set(DEVICE_IFACE, 'AvailableConnections', connections) |
2798 | + |
2799 | + main_connections.remove(connection_path) |
2800 | + settings_obj.Set(SETTINGS_IFACE, 'Connections', main_connections) |
2801 | + |
2802 | + settings_obj.EmitSignal( |
2803 | + SETTINGS_IFACE, 'ConnectionRemoved', 'o', [connection_path] |
2804 | + ) |
2805 | + |
2806 | + connection_obj = dbusmock.get_object(connection_path) |
2807 | + connection_obj.EmitSignal(CSETTINGS_IFACE, 'Removed', '', []) |
2808 | + |
2809 | + self.RemoveObject(connection_path) |
2810 | + |
2811 | + |
2812 | +@dbus.service.method(MOCK_IFACE, |
2813 | + in_signature='ss', out_signature='') |
2814 | +def RemoveActiveConnection(self, dev_path, active_connection_path): |
2815 | + '''Remove the specified ActiveConnection. |
2816 | + |
2817 | + You have to specify the device to remove the connection from, and the |
2818 | + path of the ActiveConnection. |
2819 | + |
2820 | + Please note that this does not set any global properties. |
2821 | + ''' |
2822 | + self.SetDeviceDisconnected(dev_path) |
2823 | + |
2824 | + active_connections = self.Get(MAIN_IFACE, 'ActiveConnections') |
2825 | + |
2826 | + if active_connection_path not in active_connections: |
2827 | + return |
2828 | + |
2829 | + active_connections.remove(dbus.ObjectPath(active_connection_path)) |
2830 | + self.SetProperty( |
2831 | + MAIN_OBJ, MAIN_IFACE, 'ActiveConnections', active_connections |
2832 | + ) |
2833 | + |
2834 | + self.RemoveObject(active_connection_path) |
2835 | + |
2836 | + |
2837 | +@dbus.service.method(SETTINGS_IFACE, |
2838 | + in_signature='a{sa{sv}}', out_signature='o') |
2839 | +def SettingsAddConnection(self, connection_settings): |
2840 | + '''Add a connection. |
2841 | + |
2842 | + connection_settings is a String String Variant Map Map. See |
2843 | + https://developer.gnome.org/NetworkManager/0.9/spec.html |
2844 | + #type-String_String_Variant_Map_Map |
2845 | + |
2846 | + If you omit uuid, this method adds one for you. |
2847 | + ''' |
2848 | + |
2849 | + if 'uuid' not in connection_settings['connection']: |
2850 | + connection_settings['connection']['uuid'] = str(uuid.uuid4()) |
2851 | + |
2852 | + NM = dbusmock.get_object(MAIN_OBJ) |
2853 | + settings_obj = dbusmock.get_object(SETTINGS_OBJ) |
2854 | + main_connections = settings_obj.ListConnections() |
2855 | + |
2856 | + # Mimic how NM names connections |
2857 | + connection_name = str(len(main_connections)) |
2858 | + |
2859 | + connection_path = SETTINGS_OBJ + '/' + connection_name |
2860 | + |
2861 | + if connection_path in main_connections: |
2862 | + raise dbus.exceptions.DBusException( |
2863 | + 'Connection %s already exists' % connection_path, |
2864 | + name=MAIN_IFACE + '.AlreadyExists',) |
2865 | + |
2866 | + self.AddObject( |
2867 | + connection_path, |
2868 | + CSETTINGS_IFACE, |
2869 | + { |
2870 | + 'Settings': dbus.Dictionary( |
2871 | + connection_settings, signature='sa{sv}' |
2872 | + ), |
2873 | + 'Secrets': dbus.Dictionary({}, signature='sa{sv}'), |
2874 | + }, |
2875 | + [ |
2876 | + ('Delete', '', '', 'self.ConnectionDelete("%s")' % |
2877 | + connection_path), |
2878 | + ( |
2879 | + 'GetSettings', '', 'a{sa{sv}}', |
2880 | + "ret = self.Get('%s', 'Settings')" % CSETTINGS_IFACE |
2881 | + ), |
2882 | + ( |
2883 | + 'GetSecrets', 's', 'a{sa{sv}}', |
2884 | + "ret = self.Get('%s', 'Secrets')" % CSETTINGS_IFACE |
2885 | + ), |
2886 | + ( |
2887 | + 'Update', 'a{sa{sv}}', '', |
2888 | + 'self.ConnectionUpdate("%s", args[0])' % connection_path |
2889 | + ), |
2890 | + ] |
2891 | + ) |
2892 | + |
2893 | + main_connections.append(connection_path) |
2894 | + settings_obj.Set(SETTINGS_IFACE, 'Connections', main_connections) |
2895 | + |
2896 | + settings_obj.EmitSignal( |
2897 | + SETTINGS_IFACE, 'NewConnection', 'o', [connection_path] |
2898 | + ) |
2899 | + |
2900 | + auto_connect = False |
2901 | + if 'autoconnect' in connection_settings['connection']: |
2902 | + auto_connect = connection_settings['connection']['autoconnect'] |
2903 | + |
2904 | + if auto_connect: |
2905 | + dev = None |
2906 | + devices = NM.GetDevices() |
2907 | + |
2908 | + # Grab the first device. |
2909 | + if len(devices) > 0: |
2910 | + dev = devices[0] |
2911 | + |
2912 | + if dev: |
2913 | + activate_connection(NM, connection_path, dev, connection_path) |
2914 | + |
2915 | + return connection_path |
2916 | + |
2917 | + |
2918 | +@dbus.service.method(CSETTINGS_IFACE, |
2919 | + in_signature='oa{sa{sv}}', out_signature='') |
2920 | +def ConnectionUpdate(self, connection_path, settings): |
2921 | + '''Update settings on a connection. |
2922 | + |
2923 | + settings is a String String Variant Map Map. See |
2924 | + https://developer.gnome.org/NetworkManager/0.9/spec.html |
2925 | + #type-String_String_Variant_Map_Map |
2926 | + ''' |
2927 | + NM = dbusmock.get_object(MAIN_OBJ) |
2928 | + settings_obj = dbusmock.get_object(SETTINGS_OBJ) |
2929 | + conn_obj = dbusmock.get_object(connection_path) |
2930 | + |
2931 | + main_connections = settings_obj.ListConnections() |
2932 | + |
2933 | + if connection_path not in main_connections: |
2934 | + raise dbus.exceptions.DBusException( |
2935 | + 'Connection %s does not exist' % connection_path, |
2936 | + name=MAIN_IFACE + '.DoesNotExist',) |
2937 | + |
2938 | + conn_settings = conn_obj.Get(CSETTINGS_IFACE, 'Settings') |
2939 | + changed_settings = {} |
2940 | + for key, value in settings.items(): |
2941 | + for k, v in value.items(): |
2942 | + changed_settings[k] = v |
2943 | + |
2944 | + if key not in conn_settings: |
2945 | + conn_settings[key] = dbus.Dictionary({}, signature='sv') |
2946 | + |
2947 | + conn_settings[key][k] = v |
2948 | + |
2949 | + conn_obj.Set(CSETTINGS_IFACE, 'Settings', conn_settings) |
2950 | + |
2951 | + settings_obj.EmitSignal( |
2952 | + CSETTINGS_IFACE, 'PropertiesChanged', 'a{sv}', [changed_settings] |
2953 | + ) |
2954 | + settings_obj.EmitSignal(CSETTINGS_IFACE, 'Updated', '', []) |
2955 | + |
2956 | + auto_connect = False |
2957 | + if 'autoconnect' in settings['connection']: |
2958 | + auto_connect = settings['connection']['autoconnect'] |
2959 | + |
2960 | + if auto_connect: |
2961 | + dev = None |
2962 | + devices = NM.GetDevices() |
2963 | + |
2964 | + # Grab the first device. |
2965 | + if len(devices) > 0: |
2966 | + dev = devices[0] |
2967 | + |
2968 | + if dev: |
2969 | + activate_connection(NM, connection_path, dev, connection_path) |
2970 | + |
2971 | + return connection_path |
2972 | + |
2973 | + |
2974 | +@dbus.service.method(CSETTINGS_IFACE, |
2975 | + in_signature='o', out_signature='') |
2976 | +def ConnectionDelete(self, connection_path): |
2977 | + '''Deletes a connection. |
2978 | + |
2979 | + This also |
2980 | + * removes the deleted connection from any device, |
2981 | + * removes any active connection(s) it might be associated with, |
2982 | + * removes it from the Settings interface, |
2983 | + * as well as deletes the object from the mock. |
2984 | + |
2985 | + Note: If this was the only active connection, we change the global |
2986 | + connection state. |
2987 | + ''' |
2988 | + NM = dbusmock.get_object(MAIN_OBJ) |
2989 | + settings_obj = dbusmock.get_object(SETTINGS_OBJ) |
2990 | + |
2991 | + # Find the associated active connection(s). |
2992 | + active_connections = NM.Get(MAIN_IFACE, 'ActiveConnections') |
2993 | + associated_active_connections = [] |
2994 | + for ac in active_connections: |
2995 | + ac_obj = dbusmock.get_object(ac) |
2996 | + ac_con = ac_obj.Get(ACTIVE_CONNECTION_IFACE, 'Connection') |
2997 | + if ac_con == connection_path: |
2998 | + associated_active_connections.append(ac) |
2999 | + |
3000 | + # We found that the connection we are deleting are associated to all |
3001 | + # active connections and subsequently set the global state to |
3002 | + # disconnected. |
3003 | + if len(active_connections) == len(associated_active_connections): |
3004 | + self.SetGlobalConnectionState(NMState.NM_STATE_DISCONNECTED) |
3005 | + |
3006 | + # Remove the connection from all associated devices. |
3007 | + # We also remove all associated active connections. |
3008 | + for dev_path in NM.GetDevices(): |
3009 | + dev_obj = dbusmock.get_object(dev_path) |
3010 | + connections = dev_obj.Get(DEVICE_IFACE, 'AvailableConnections') |
3011 | + |
3012 | + for ac in associated_active_connections: |
3013 | + NM.RemoveActiveConnection(dev_path, ac) |
3014 | + |
3015 | + if connection_path not in connections: |
3016 | + continue |
3017 | + |
3018 | + connections.remove(dbus.ObjectPath(connection_path)) |
3019 | + dev_obj.Set(DEVICE_IFACE, 'AvailableConnections', connections) |
3020 | + |
3021 | + # Remove the connection from the settings interface |
3022 | + main_connections = settings_obj.ListConnections() |
3023 | + if connection_path not in main_connections: |
3024 | + return |
3025 | + main_connections.remove(connection_path) |
3026 | + settings_obj.Set(SETTINGS_IFACE, 'Connections', main_connections) |
3027 | + settings_obj.EmitSignal( |
3028 | + SETTINGS_IFACE, 'ConnectionRemoved', 'o', [connection_path] |
3029 | + ) |
3030 | + |
3031 | + # Remove the connection from the mock |
3032 | + connection_obj = dbusmock.get_object(connection_path) |
3033 | + connection_obj.EmitSignal(CSETTINGS_IFACE, 'Removed', '', []) |
3034 | + self.RemoveObject(connection_path) |
3035 | |
3036 | === modified file 'tests/autopilot/ubuntu_system_settings/tests/test_wifi.py' |
3037 | --- tests/autopilot/ubuntu_system_settings/tests/test_wifi.py 2015-04-08 12:15:56 +0000 |
3038 | +++ tests/autopilot/ubuntu_system_settings/tests/test_wifi.py 2015-07-01 18:56:14 +0000 |
3039 | @@ -6,8 +6,13 @@ |
3040 | # by the Free Software Foundation. |
3041 | |
3042 | from __future__ import absolute_import |
3043 | + |
3044 | +import dbus |
3045 | + |
3046 | from autopilot.matchers import Eventually |
3047 | -from dbusmock.templates.networkmanager import DEVICE_IFACE |
3048 | +from dbusmock.templates.networkmanager import ( |
3049 | + DEVICE_IFACE, NM80211ApSecurityFlags |
3050 | +) |
3051 | from testtools.matchers import Equals |
3052 | from time import sleep |
3053 | from ubuntu_system_settings.tests import WifiBaseTestCase |
3054 | @@ -18,6 +23,12 @@ |
3055 | class WifiTestCase(WifiBaseTestCase): |
3056 | """Tests for Language Page""" |
3057 | |
3058 | + def setUp(self): |
3059 | + super(WifiTestCase, self).setUp() |
3060 | + |
3061 | + self.wifi_page._scroll_to_and_click = \ |
3062 | + self.main_view.scroll_to_and_click |
3063 | + |
3064 | def test_wifi_page_title_is_correct(self): |
3065 | """Checks whether Wifi page is available""" |
3066 | self.assertThat( |
3067 | @@ -32,14 +43,62 @@ |
3068 | self.wifi_page.enable_wireless() |
3069 | dialog = self.wifi_page.connect_to_hidden_network( |
3070 | 'test_ap', |
3071 | - scroll_to_and_click=self.main_view |
3072 | - .scroll_to_and_click) |
3073 | - |
3074 | - # allow backend to set up listeners |
3075 | - sleep(0.3) |
3076 | - |
3077 | - if dialog: |
3078 | - dialog.wait_until_destroyed() |
3079 | + password='abcdefgh',) |
3080 | + |
3081 | + # allow backend to set up listeners |
3082 | + sleep(0.3) |
3083 | + |
3084 | + if dialog: |
3085 | + dialog.wait_until_destroyed() |
3086 | + |
3087 | + def test_connect_to_eduroam(self): |
3088 | + if not self.wifi_page.have_wireless(): |
3089 | + self.skipTest('Cannot test wireless since it cannot be enabled') |
3090 | + self.addCleanup( |
3091 | + self.wifi_page._set_wireless, self.wifi_page.get_wireless()) |
3092 | + self.wifi_page.enable_wireless() |
3093 | + self.create_access_point( |
3094 | + 'eduroam', 'eduroam', |
3095 | + security=NM80211ApSecurityFlags.NM_802_11_AP_SEC_KEY_MGMT_802_1X |
3096 | + ) |
3097 | + |
3098 | + dialog = self.wifi_page.connect_to_hidden_network( |
3099 | + 'eduroam', |
3100 | + username='student', |
3101 | + password='abcdefgh', |
3102 | + security='wpa-ep', |
3103 | + auth='peap', |
3104 | + protocol='mschapv2', |
3105 | + ) |
3106 | + |
3107 | + # allow backend to set up listeners |
3108 | + sleep(0.3) |
3109 | + |
3110 | + if dialog: |
3111 | + dialog.wait_until_destroyed() |
3112 | + |
3113 | + dev = dbus.Interface(self.dbus_con.get_object( |
3114 | + 'org.freedesktop.NetworkManager', self.device_path), |
3115 | + 'org.freedesktop.DBus.Properties') |
3116 | + |
3117 | + conn_obj = dev.Get( |
3118 | + 'org.freedesktop.NetworkManager.Device', 'AvailableConnections' |
3119 | + )[0] |
3120 | + conn = dbus.Interface(self.dbus_con.get_object( |
3121 | + 'org.freedesktop.NetworkManager', conn_obj), |
3122 | + 'org.freedesktop.NetworkManager.Settings.Connection') |
3123 | + |
3124 | + conn_settings = conn.GetSettings() |
3125 | + wconn = conn_settings['connection'] |
3126 | + w802_11_sec = conn_settings['802-11-wireless-security'] |
3127 | + w802_1x = conn_settings['802-1x'] |
3128 | + |
3129 | + self.assertEquals(wconn['type'], '802-11-wireless') |
3130 | + self.assertEquals(w802_11_sec['key-mgmt'], 'wpa-eap') |
3131 | + self.assertIn('peap', w802_1x['eap']) |
3132 | + self.assertEquals(w802_1x['identity'], 'student') |
3133 | + self.assertEquals(w802_1x['password'], 'abcdefgh') |
3134 | + self.assertEquals(w802_1x['phase2-auth'], 'mschapv2') |
3135 | |
3136 | def test_connect_to_nonexistant_hidden_network(self): |
3137 | if not self.wifi_page.have_wireless(): |
3138 | @@ -49,8 +108,8 @@ |
3139 | self.wifi_page.enable_wireless() |
3140 | dialog = self.wifi_page.connect_to_hidden_network( |
3141 | 'n/a', |
3142 | - scroll_to_and_click=self.main_view |
3143 | - .scroll_to_and_click) |
3144 | + password='abcdefgh', |
3145 | + ) |
3146 | |
3147 | # allow backend to set up listeners |
3148 | sleep(0.3) |
3149 | @@ -84,8 +143,7 @@ |
3150 | self.wifi_page.enable_wireless() |
3151 | dialog = self.wifi_page.connect_to_hidden_network( |
3152 | 'test_ap', security='wpa', password='abcdefgh', |
3153 | - scroll_to_and_click=self.main_view |
3154 | - .scroll_to_and_click) |
3155 | + ) |
3156 | |
3157 | # allow backend to set up listeners |
3158 | sleep(0.3) |
3159 | @@ -102,9 +160,10 @@ |
3160 | self.wifi_page._set_wireless, self.wifi_page.get_wireless()) |
3161 | self.wifi_page.enable_wireless() |
3162 | dialog = self.wifi_page.connect_to_hidden_network( |
3163 | - 'test_ap', security='wpa', password='abcdefgh', |
3164 | - scroll_to_and_click=self.main_view |
3165 | - .scroll_to_and_click) |
3166 | + 'test_ap', |
3167 | + security='wpa', |
3168 | + password='abcdefgh', |
3169 | + ) |
3170 | |
3171 | # allow backend to set up listeners |
3172 | sleep(0.3) |
3173 | @@ -120,10 +179,7 @@ |
3174 | self.addCleanup( |
3175 | self.wifi_page._set_wireless, self.wifi_page.get_wireless()) |
3176 | self.wifi_page.enable_wireless() |
3177 | - dialog = self.wifi_page.connect_to_hidden_network( |
3178 | - 'foo', |
3179 | - scroll_to_and_click=self.main_view |
3180 | - .scroll_to_and_click) |
3181 | + dialog = self.wifi_page.connect_to_hidden_network('foo',) |
3182 | |
3183 | # allow backend to set up listeners |
3184 | sleep(0.3) |
3185 | @@ -131,8 +187,7 @@ |
3186 | dialog.cancel() |
3187 | |
3188 | self.assertThat( |
3189 | - lambda: |
3190 | - len(self.active_connection_mock.GetMethodCalls('Delete')), |
3191 | + lambda: len(self.active_connection_mock.GetMethodCalls('Delete')), |
3192 | Eventually(Equals(1))) |
3193 | |
3194 | def test_connect_to_hidden_network_dialog_visibility(self): |
3195 | @@ -148,8 +203,6 @@ |
3196 | Eventually(Equals(False)), 'other net dialog not hidden') |
3197 | |
3198 | def test_remove_previous_network(self): |
3199 | - click_method = self.main_view.scroll_to_and_click |
3200 | - |
3201 | access_points = ['Series of Tubes', 'dev/null', 'Mi-Fi', |
3202 | 'MonkeySphere'] |
3203 | |
3204 | @@ -158,16 +211,14 @@ |
3205 | self.dbusmock.AddWiFiConnection( |
3206 | self.device_path, 'Mock_Con%d' % idx, ssid, '') |
3207 | |
3208 | - self.wifi_page.remove_previous_network( |
3209 | - access_points[0], scroll_to_and_click=click_method) |
3210 | + self.wifi_page.remove_previous_network(access_points[0],) |
3211 | |
3212 | self.main_view.go_back() |
3213 | |
3214 | # wait for ui to update |
3215 | sleep(2) |
3216 | |
3217 | - self.wifi_page.remove_previous_network( |
3218 | - access_points[2], scroll_to_and_click=click_method) |
3219 | + self.wifi_page.remove_previous_network(access_points[2],) |
3220 | |
3221 | # We cannot make any assertions, because connection deletion |
3222 | # is currently not supported. |
This is really great. Thanks for proposing this.
I've added some comments, but they're mostly about your decision to make the Dialog an ItemPage. If this is not crucial to the implementation of support for 802-1x, we need to revert it.
Also, if cert picking is not implemented, please remove all components that are unused. Please leave the UI bits in, but hide it with "visible: showAllUI". This way we can get strings translated.
Thanks again.