~facundo/magicicada-client/changes-for-xenial

« back to all changes in this revision

Viewing changes to ubuntuone/keyring.py

  • Committer: Magicicada Bot
  • Author(s): Natalia
  • Date: 2016-05-30 15:43:30 UTC
  • mfrom: (1418.1.21 no-sso-client)
  • Revision ID: magicicada_bot-20160530154330-b4his4s3wlucu7zv
[r=facundo] - Decouple client code from ubuntu-sso-client code. Copied and made an initial cleanup on the networkstate, utils and keyring modules.
- Removed completely dependencies with oauthlibs.
- Moved tests/ folder to inside ubuntuone/ proper folders.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
#
 
3
# Copyright 2011-2012 Canonical Ltd.
 
4
# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
 
5
#
 
6
# This program is free software: you can redistribute it and/or modify it
 
7
# under the terms of the GNU General Public License version 3, as published
 
8
# by the Free Software Foundation.
 
9
#
 
10
# This program is distributed in the hope that it will be useful, but
 
11
# WITHOUT ANY WARRANTY; without even the implied warranties of
 
12
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
13
# PURPOSE.  See the GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License along
 
16
# with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
#
 
18
# In addition, as a special exception, the copyright holders give
 
19
# permission to link the code of portions of this program with the
 
20
# OpenSSL library under certain conditions as described in each
 
21
# individual source file, and distribute linked combinations
 
22
# including the two.
 
23
# You must obey the GNU General Public License in all respects
 
24
# for all of the code used other than OpenSSL.  If you modify
 
25
# file(s) with this exception, you may extend this exception to your
 
26
# version of the file(s), but you are not obligated to do so.  If you
 
27
# do not wish to do so, delete this exception statement from your
 
28
# version.  If you delete this exception statement from all source
 
29
# files in the program, then also delete it here.
 
30
 
 
31
"""Handle keys in the local kerying."""
 
32
 
 
33
from __future__ import unicode_literals
 
34
 
 
35
import logging
 
36
import socket
 
37
import sys
 
38
 
 
39
try:
 
40
    from urllib.parse import parse_qsl, quote, urlencode
 
41
except ImportError:
 
42
    from urllib import quote, urlencode
 
43
    from urlparse import parse_qsl
 
44
 
 
45
from twisted.internet.defer import inlineCallbacks, returnValue
 
46
 
 
47
from ubuntuone.clientdefs import NAME
 
48
from ubuntuone.utils import compat
 
49
from ubuntuone.utils.txsecrets import SecretService
 
50
 
 
51
 
 
52
logger = logging.getLogger(__name__)
 
53
 
 
54
TOKEN_SEPARATOR = ' @ '
 
55
SEPARATOR_REPLACEMENT = ' AT '
 
56
 
 
57
 
 
58
def gethostname():
 
59
    """Get the hostname, return the name as unicode."""
 
60
    sys_encoding = sys.getfilesystemencoding()
 
61
    hostname = socket.gethostname()
 
62
    if isinstance(hostname, compat.binary_type):
 
63
        return hostname.decode(sys_encoding)
 
64
    return hostname
 
65
 
 
66
 
 
67
def get_token_name(app_name):
 
68
    """Build the token name.. Return an unicode."""
 
69
    computer_name = gethostname()
 
70
    computer_name = computer_name.replace(TOKEN_SEPARATOR,
 
71
                                          SEPARATOR_REPLACEMENT)
 
72
 
 
73
    assert isinstance(computer_name, compat.text_type)
 
74
    assert isinstance(computer_name, compat.text_type)
 
75
 
 
76
    return TOKEN_SEPARATOR.join((app_name, computer_name))
 
77
 
 
78
 
 
79
class Keyring(object):
 
80
    """A Keyring for a given application name."""
 
81
 
 
82
    def __init__(self):
 
83
        """Initialize this instance."""
 
84
        self.service = SecretService()
 
85
 
 
86
    def _get_keyring_attr(self, app_name):
 
87
        """Build the keyring attributes for this credentials."""
 
88
        attr = {"key-type": "%s credentials" % NAME,
 
89
                "token-name": get_token_name(app_name)}
 
90
        return attr
 
91
 
 
92
    @inlineCallbacks
 
93
    def _find_keyring_item(self, app_name, attr=None):
 
94
        """Return the keyring item or None if not found."""
 
95
        if attr is None:
 
96
            attr = self._get_keyring_attr(app_name)
 
97
        logger.debug("Finding all items for app_name %r.", app_name)
 
98
        items = yield self.service.search_items(attr)
 
99
        if len(items) == 0:
 
100
            # if no items found, return None
 
101
            logger.debug("No items found!")
 
102
            returnValue(None)
 
103
 
 
104
        logger.debug("Returning first item found.")
 
105
        returnValue(items[0])
 
106
 
 
107
    @inlineCallbacks
 
108
    def set_credentials(self, app_name, cred):
 
109
        """Set the credentials."""
 
110
        # Creates the secret from the credentials
 
111
        secret = urlencode(cred)
 
112
 
 
113
        attr = self._get_keyring_attr(app_name)
 
114
        # Add our credentials to the keyring
 
115
        yield self.service.open_session()
 
116
        collection = yield self.service.get_default_collection()
 
117
        yield collection.create_item(app_name, attr, secret, True)
 
118
 
 
119
    @inlineCallbacks
 
120
    def get_credentials(self, app_name):
 
121
        """A deferred with the secret in a dictionary."""
 
122
        # If we have no attributes, return None
 
123
        logger.debug("Getting credentials for %r.", app_name)
 
124
        yield self.service.open_session()
 
125
        item = yield self._find_keyring_item(app_name)
 
126
        if item is not None:
 
127
            logger.debug("Parsing secret.")
 
128
            secret = yield item.get_value()
 
129
            returnValue(dict(parse_qsl(secret)))
 
130
 
 
131
        # nothing was found
 
132
        returnValue(None)
 
133
 
 
134
    @inlineCallbacks
 
135
    def delete_credentials(self, app_name):
 
136
        """Delete a set of credentials from the keyring."""
 
137
        attr = self._get_keyring_attr(app_name)
 
138
        # Add our credentials to the keyring
 
139
        yield self.service.open_session()
 
140
        collection = yield self.service.get_default_collection()
 
141
        yield collection.create_item(app_name, attr, "secret!", True)
 
142
 
 
143
        item = yield self._find_keyring_item(app_name)
 
144
        if item is not None:
 
145
            yield item.delete()