helper_passwords.py 16.3 KB
Newer Older
Cédric Bellegarde's avatar
Cédric Bellegarde committed
1
# Copyright (c) 2017-2019 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
2 3 4 5 6 7 8 9 10 11 12 13 14
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import gi
gi.require_version('Secret', '1')
15
from gi.repository import Secret, GLib
16

17
from eolie.logger import Logger
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32


class PasswordsHelper:
    """
        Simpler helper for Secret
    """

    def __init__(self):
        """
            Init helper
        """
        self.__secret = None
        Secret.Service.get(Secret.ServiceFlags.NONE, None,
                           self.__on_get_secret)

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    def get_all(self, callback, *args):
        """
            Call function
            @param callback as function
            @param args
        """
        try:
            self.__wait_for_secret(self.get_all, callback, *args)
            SecretSchema = {
                "type": Secret.SchemaAttributeType.STRING,
            }
            SecretAttributes = {
                "type": "eolie web login",
            }
            schema = Secret.Schema.new("org.gnome.Eolie",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            self.__secret.search(schema, SecretAttributes,
                                 Secret.SearchFlags.ALL,
                                 None,
                                 self.__on_secret_search,
                                 None,
                                 callback,
                                 *args)
        except Exception as e:
58
            Logger.debug("PasswordsHelper::get_all(): %s", e)
59

60
    def get(self, form_uri, userform, passform, callback, *args):
61 62
        """
            Call function
63
            @param form_uri as str
64 65
            @param userform as str
            @param passform as str
66 67 68 69
            @param callback as function
            @param args
        """
        try:
70
            self.__wait_for_secret(self.get, form_uri, userform,
71
                                   passform, callback, *args)
72 73
            SecretSchema = {
                "type": Secret.SchemaAttributeType.STRING,
74 75
                "formSubmitURL": Secret.SchemaAttributeType.STRING,
                "userform": Secret.SchemaAttributeType.STRING,
76 77 78
            }
            SecretAttributes = {
                "type": "eolie web login",
79
                "formSubmitURL": form_uri,
80
                "userform": userform,
81
            }
82 83 84 85
            if passform is not None:
                SecretSchema["passform"] = Secret.SchemaAttributeType.STRING
                SecretAttributes["passform"] = passform

86 87 88 89
            schema = Secret.Schema.new("org.gnome.Eolie",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            self.__secret.search(schema, SecretAttributes,
90
                                 Secret.SearchFlags.ALL,
91 92
                                 None,
                                 self.__on_secret_search,
93
                                 form_uri,
94 95 96
                                 callback,
                                 *args)
        except Exception as e:
97
            Logger.debug("PasswordsHelper::get(): %s", e)
98

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    def get_sync(self, callback, *args):
        """
            Get sync password
            @param callback as function
        """
        try:
            self.__wait_for_secret(self.get_sync, callback, *args)
            SecretSchema = {
                "sync": Secret.SchemaAttributeType.STRING
            }
            SecretAttributes = {
                "sync": "mozilla"
            }
            schema = Secret.Schema.new("org.gnome.Eolie",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            self.__secret.search(schema, SecretAttributes,
                                 Secret.SearchFlags.NONE,
                                 None,
                                 self.__on_secret_search,
119
                                 None,
120 121 122
                                 callback,
                                 *args)
        except Exception as e:
123
            Logger.debug("PasswordsHelper::get_sync(): %s", e)
124

125
    def store(self, user_form_name, user_form_value, pass_form_name,
126
              pass_form_value, hostname_uri, form_uri, uuid, callback, *args):
127 128
        """
            Store password
129 130 131 132
            @param user_form_name as str
            @param user_form_value as str
            @param pass_form_name as str
            @param pass_form_value as str
133
            @param hostname_uri as str
134
            @param form_uri as str
135
            @param uuid as str
136 137
            @param callback as function
        """
138 139 140
        # seems to happen, thanks firefox
        if form_uri is None:
            return
141 142
        try:
            self.__wait_for_secret(self.store,
143 144 145 146
                                   user_form_name,
                                   user_form_value,
                                   pass_form_name,
                                   pass_form_value,
147
                                   hostname_uri,
148 149 150 151
                                   form_uri,
                                   uuid,
                                   callback,
                                   *args)
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
            # Clear item if exists
            SecretSchema = {
                "type": Secret.SchemaAttributeType.STRING,
                "login": Secret.SchemaAttributeType.STRING,
                "hostname": Secret.SchemaAttributeType.STRING,
                "formSubmitURL": Secret.SchemaAttributeType.STRING,
                "userform": Secret.SchemaAttributeType.STRING,
                "passform": Secret.SchemaAttributeType.STRING,
            }
            SecretAttributes = {
                "type": "eolie web login",
                "login": user_form_value,
                "hostname": hostname_uri,
                "formSubmitURL": form_uri,
                "userform": user_form_name,
                "passform": pass_form_name
            }
            schema = Secret.Schema.new("org.gnome.Eolie",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            self.__secret.search(schema,
                                 SecretAttributes,
                                 Secret.SearchFlags.ALL,
                                 None,
                                 self.__on_search_clear,
                                 callback,
                                 *args)

180 181
            schema_string = "org.gnome.Eolie: %s > %s" % (user_form_value,
                                                          hostname_uri)
182 183
            SecretSchema = {
                "type": Secret.SchemaAttributeType.STRING,
184
                "uuid": Secret.SchemaAttributeType.STRING,
185 186
                "login": Secret.SchemaAttributeType.STRING,
                "hostname": Secret.SchemaAttributeType.STRING,
187 188 189
                "formSubmitURL": Secret.SchemaAttributeType.STRING,
                "userform": Secret.SchemaAttributeType.STRING,
                "passform": Secret.SchemaAttributeType.STRING,
190 191 192
            }
            SecretAttributes = {
                "type": "eolie web login",
193
                "uuid": uuid,
194
                "login": user_form_value,
195 196
                "hostname": hostname_uri,
                "formSubmitURL": form_uri,
197 198
                "userform": user_form_name,
                "passform": pass_form_name
199
            }
200

201 202 203 204 205 206
            schema = Secret.Schema.new("org.gnome.Eolie",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            Secret.password_store(schema, SecretAttributes,
                                  Secret.COLLECTION_DEFAULT,
                                  schema_string,
207
                                  pass_form_value,
208 209 210
                                  None,
                                  callback)
        except Exception as e:
211
            Logger.debug("PasswordsHelper::store(): %s", e)
212

213
    def store_sync(self, login, password, callback=None, *args):
214
        """
215
            Store Firefox Sync password
216 217 218
            @param login as str
            @param password as str
            @param callback as function
Cédric Bellegarde's avatar
Cédric Bellegarde committed
219
            @param data
220 221
        """
        try:
222
            self.__wait_for_secret(self.store_sync,
223 224 225 226 227 228 229 230 231 232 233 234
                                   login,
                                   password,
                                   callback)
            schema_string = "org.gnome.Eolie.sync"
            SecretSchema = {
                "sync": Secret.SchemaAttributeType.STRING,
                "login": Secret.SchemaAttributeType.STRING,
            }
            schema = Secret.Schema.new("org.gnome.Eolie",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            SecretAttributes = {
Cédric Bellegarde's avatar
Cédric Bellegarde committed
235 236
                "sync": "mozilla",
                "login": login,
237 238 239 240 241 242
            }
            Secret.password_store(schema, SecretAttributes,
                                  Secret.COLLECTION_DEFAULT,
                                  schema_string,
                                  password,
                                  None,
Cédric Bellegarde's avatar
Cédric Bellegarde committed
243
                                  callback,
Cédric Bellegarde's avatar
Cédric Bellegarde committed
244
                                  *args)
245
        except Exception as e:
246
            Logger.debug("PasswordsHelper::store_sync(): %s", e)
247

Cédric Bellegarde's avatar
Cédric Bellegarde committed
248
    def clear(self, uuid, callback=None, *args):
249 250 251
        """
            Clear password
            @param uuid as str
Cédric Bellegarde's avatar
Cédric Bellegarde committed
252
            @param callback as function
253 254
        """
        try:
255
            self.__wait_for_secret(self.clear, uuid)
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
            SecretSchema = {
                "type": Secret.SchemaAttributeType.STRING,
                "uuid": Secret.SchemaAttributeType.STRING
            }
            SecretAttributes = {
                "type": "eolie web login",
                "uuid": uuid
            }
            schema = Secret.Schema.new("org.gnome.Eolie",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            self.__secret.search(schema,
                                 SecretAttributes,
                                 Secret.SearchFlags.ALL,
                                 None,
271
                                 self.__on_search_clear,
Cédric Bellegarde's avatar
Cédric Bellegarde committed
272 273
                                 callback,
                                 *args)
274
        except Exception as e:
275
            Logger.debug("PasswordsHelper::clear(): %s", e)
276

277
    def clear_sync(self, callback, *args):
278 279
        """
            Clear sync secrets
280
            @param callback as function
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
        """
        try:
            SecretSchema = {
                "sync": Secret.SchemaAttributeType.STRING
            }
            SecretAttributes = {
                "sync": "mozilla"
            }
            schema = Secret.Schema.new("org.gnome.Eolie",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            self.__secret.search(schema,
                                 SecretAttributes,
                                 Secret.SearchFlags.ALL,
                                 None,
296
                                 self.__on_search_clear,
297 298
                                 callback,
                                 *args)
299
        except Exception as e:
300
            Logger.debug("PasswordsHelper::clear_sync(): %s", e)
301

302
    def clear_all(self):
303 304 305 306
        """
            Clear passwords
        """
        try:
307
            self.__wait_for_secret(self.clear_all)
308 309 310 311 312 313 314 315 316 317 318 319 320
            SecretSchema = {
                "type": Secret.SchemaAttributeType.STRING
            }
            SecretAttributes = {
                "type": "eolie web login"
            }
            schema = Secret.Schema.new("org.gnome.Eolie",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            self.__secret.search(schema,
                                 SecretAttributes,
                                 Secret.SearchFlags.ALL,
                                 None,
321
                                 self.__on_search_clear)
322
        except Exception as e:
323
            Logger.debug("PasswordsHelper::clear_all(): %s", e)
324

325 326 327
#######################
# PRIVATE             #
#######################
328 329 330 331 332 333 334 335 336
    def __wait_for_secret(self, call, *args):
        """
            Wait for secret
            @param call as function to call
            @param args
            @raise exception if waiting
        """
        # Wait for secret
        if self.__secret is None:
337
            GLib.timeout_add(250, call, *args)
338 339 340
        if self.__secret in [None, -1]:
            raise Exception("Waiting Secret service")

341
    def __on_load_secret(self, source, result, form_uri,
342
                         index, count, callback, *args):
343 344 345 346
        """
            Set username/password input
            @param source as GObject.Object
            @param result as Gio.AsyncResult
347
            @param form_uri as str
348 349
            @param index as int
            @param count as int
350 351 352
            @param callback as function
            @param args
        """
353 354
        secret = source.get_secret()
        if secret is not None:
355
            attributes = source.get_attributes()
356 357 358 359
            keys = attributes.keys()
            # We ignore old Eolie passwords
            if "userform" in keys or\
                    "sync" in keys:
360
                callback(attributes,
361
                         secret.get().decode('utf-8'),
362
                         form_uri,
363 364
                         index,
                         count,
365 366
                         *args)
            else:
367
                callback(None, None, form_uri, 0, 0, *args)
368
        else:
369
            callback(None, None, form_uri, 0, 0, *args)
370

371
    def __on_search_clear(self, source, result, callback=None, *args):
372 373 374 375 376 377 378 379 380 381
        """
            Clear passwords
            @param source as GObject.Object
            @param result as Gio.AsyncResult
        """
        try:
            if result is not None:
                items = source.search_finish(result)
                for item in items:
                    item.delete(None, None)
Cédric Bellegarde's avatar
Cédric Bellegarde committed
382 383
            if callback is not None:
                callback(*args)
384
        except Exception as e:
385
            Logger.debug("PasswordsHelper::__on_search_clear(): %s", e)
386

387
    def __on_secret_search(self, source, result, form_uri, callback, *args):
388 389 390 391
        """
            Set username/password input
            @param source as GObject.Object
            @param result as Gio.AsyncResult
392
            @param form_uri as str
393 394 395 396 397 398
            @param callback as function
            @param args
        """
        try:
            if result is not None:
                items = self.__secret.search_finish(result)
399 400
                count = len(items)
                index = 0
401 402 403
                for item in items:
                    item.load_secret(None,
                                     self.__on_load_secret,
404
                                     form_uri,
405 406
                                     index,
                                     count,
407 408
                                     callback,
                                     *args)
409
                    index += 1
410
                if not items:
411
                    callback(None, None, form_uri, 0, 0, *args)
412
            else:
413
                callback(None, None, form_uri, 0, 0, *args)
414
        except Exception as e:
415
            Logger.debug("PasswordsHelper::__on_secret_search(): %s", e)
416
            callback(None, None, form_uri, 0, 0, *args)
417 418 419 420 421 422 423 424 425 426

    def __on_get_secret(self, source, result):
        """
            Store secret proxy
            @param source as GObject.Object
            @param result as Gio.AsyncResult
        """
        try:
            self.__secret = Secret.Service.get_finish(result)
        except Exception as e:
427
            self.__secret = -1
428
            Logger.debug("PasswordsHelper::__on_get_secret(): %s", e)