Test Failed
Push — beta ( 0deaa1...735b0d )
by Dean
03:31
created

OpenSSL._standard_has_contexts()   A

Complexity

Conditions 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
ccs 0
cts 0
cp 0
cc 2
crap 6
1 1
from plugin.core.libraries.tests.core.base import BaseTest
2
3 1
import logging
4
5 1
log = logging.getLogger(__name__)
6
7
8 1
class OpenSSL(BaseTest):
9 1
    name = 'openssl'
10 1
    optional = True
11
12 1
    @classmethod
13
    def test_import(cls):
14
        standard_version = cls._standard_version()
15
        standard_contexts = cls._standard_has_contexts()
16
17
        bundled_version = cls._bundled_version()
18
19
        libraries = {
20
            'standard': {
21
                'version': standard_version,
22
                'contexts': standard_contexts
23
            },
24
25
            'bundled': {
26
                'version': bundled_version
27
            }
28
        }
29
30
        # Check if we should use the standard ssl library
31
        if standard_contexts and (bundled_version is None or (standard_version and standard_version > bundled_version)):
32
            return {
33
                'type': 'standard',
34
                'libraries': libraries,
35
36
                'versions': {
37
                    'openssl': standard_version
38
                }
39
            }
40
41
        # Test pyOpenSSL availability
42
        import OpenSSL.SSL
43
44
        # Try construct SSL context
45
        ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
46
47 1
        # Ensure library has SNI support
48
        cnx = OpenSSL.SSL.Connection(ctx)
49
50 1
        if not hasattr(cnx, 'set_tlsext_host_name'):
51 1
            raise Exception('Missing SNI extension')
52 1
53
        # Ensure binding can be imported
54
        from cryptography.hazmat.bindings.openssl.binding import Binding
55
        assert Binding
56
57
        # Ensure secure connections work with requests
58 1
        from requests.packages.urllib3.contrib.pyopenssl import inject_into_urllib3
59 1
        import requests
60
61
        inject_into_urllib3()
62
63
        try:
64
            requests.head('https://api-v2launch.trakt.tv', timeout=3)
65
        except requests.RequestException, ex:
66
            # Ignore failed requests (server error, network problem, etc..)
67
            log.warn('Request failed: %s', ex, exc_info=True)
68
69
        return {
70
            'type': 'bundled',
71
            'libraries': libraries,
72
73
            'versions': {
74
                'openssl': bundled_version,
75
                'pyopenssl': getattr(OpenSSL, '__version__', None)
76
            }
77
        }
78
79
    @classmethod
80
    def on_success(cls, metadata):
81
        libraries = metadata['libraries']
82
83
        if not libraries['standard']['contexts']:
84
            log.debug('Standard SSL library doesn\'t support "SSLContext"')
85
86
        # Initialize ssl library
87
        if metadata['type'] == 'bundled':
88
            if libraries['bundled']['version'] > libraries['standard']['version']:
89
                log.debug('Standard SSL library is out of date')
90
91
            # Inject pyOpenSSL into requests
92
            log.debug('Using bundled SSL library (pyOpenSSL)')
93
94
            try:
95
                from requests.packages.urllib3.contrib.pyopenssl import inject_into_urllib3
96
                inject_into_urllib3()
97
            except Exception, ex:
98
                log.warn('Unable to inject pyOpenSSL into urllib3 - %s', ex, exc_info=True)
99
                return
100
        else:
101
            log.debug('Using standard SSL library (ssl)')
102
103
        # Enable secure error reporting
104
        from plugin.core.logger.handlers.error_reporter import RAVEN
105
        RAVEN.set_protocol('threaded+requests+https')
106
107
    #
108
    # Helpers
109
    #
110
111
    @classmethod
112
    def _standard_has_contexts(cls):
113
        try:
114
            import ssl
115
            return hasattr(ssl, 'SSLContext')
116
        except Exception, ex:
117
            log.warn('Unable to check if the standard ssl library supports "SSLContext": %s', ex, exc_info=True)
118
119
        return None
120
121
    @classmethod
122
    def _standard_version(cls):
123
        try:
124
            import ssl
125
            return ssl.OPENSSL_VERSION_NUMBER
126
        except Exception, ex:
127
            log.warn('Unable to retrieve standard ssl library version: %s', ex, exc_info=True)
128
129
        return None
130
131
    @classmethod
132
    def _bundled_version(cls):
133
        try:
134
            from cryptography.hazmat.bindings.openssl.binding import Binding
135
            return Binding.lib.SSLeay()
136
        except Exception, ex:
137
            log.warn('Unable to retrieve bundled ssl library version: %s', ex, exc_info=True)
138
139
        return None
140