Only initialize the Persona IDP when actually enabled
[cascardo/ipsilon.git] / ipsilon / providers / personaidp.py
1 # Copyright (C) 2014 Ipsilon project Contributors, for license see COPYING
2
3 from __future__ import absolute_import
4
5 from ipsilon.providers.common import ProviderBase, ProviderInstaller
6 from ipsilon.util.plugin import PluginObject
7 from ipsilon.util import config as pconfig
8 from ipsilon.info.common import InfoMapping
9 from ipsilon.providers.persona.auth import Persona
10 from ipsilon.tools import files
11
12 import json
13 import M2Crypto
14 import os
15
16
17 class IdpProvider(ProviderBase):
18
19     def __init__(self, *pargs):
20         super(IdpProvider, self).__init__('persona', 'persona', *pargs)
21         self.mapping = InfoMapping()
22         self.page = None
23         self.basepath = None
24         self.key = None
25         self.key_info = None
26         self.description = """
27 Provides Persona authentication infrastructure. """
28
29         self.new_config(
30             self.name,
31             pconfig.String(
32                 'issuer domain',
33                 'The issuer domain of the Persona provider',
34                 'localhost'),
35             pconfig.String(
36                 'idp key file',
37                 'The key where the Persona key is stored.',
38                 'persona.key'),
39             pconfig.List(
40                 'allowed domains',
41                 'List of domains this IdP is willing to issue claims for.'),
42         )
43
44     @property
45     def issuer_domain(self):
46         return self.get_config_value('issuer domain')
47
48     @property
49     def idp_key_file(self):
50         return self.get_config_value('idp key file')
51
52     @property
53     def allowed_domains(self):
54         return self.get_config_value('allowed domains')
55
56     def get_tree(self, site):
57         self.page = Persona(site, self)
58         # self.admin = AdminPage(site, self)
59
60         return self.page
61
62     def init_idp(self):
63         # Init IDP data
64         try:
65             self.key = M2Crypto.RSA.load_key(self.idp_key_file,
66                                              lambda *args: None)
67         except Exception, e:  # pylint: disable=broad-except
68             self.debug('Failed to init Persona provider: %r' % e)
69             return None
70
71     def on_enable(self):
72         super(IdpProvider, self).on_enable()
73         self.init_idp()
74
75
76 class Installer(ProviderInstaller):
77
78     def __init__(self, *pargs):
79         super(Installer, self).__init__()
80         self.name = 'persona'
81         self.pargs = pargs
82
83     def install_args(self, group):
84         group.add_argument('--persona', choices=['yes', 'no'], default='yes',
85                            help='Configure Persona Provider')
86
87     def configure(self, opts, changes):
88         if opts['persona'] != 'yes':
89             return
90
91         # Check storage path is present or create it
92         path = os.path.join(opts['data_dir'], 'persona')
93         if not os.path.exists(path):
94             os.makedirs(path, 0700)
95
96         keyfile = os.path.join(path, 'persona.key')
97         exponent = 0x10001
98         key = M2Crypto.RSA.gen_key(2048, exponent)
99         key.save_key(keyfile, cipher=None)
100         key_n = 0
101         for c in key.n[4:]:
102             key_n = (key_n*256) + ord(c)
103         wellknown = dict()
104         wellknown['authentication'] = '/%s/persona/SignIn/' % opts['instance']
105         wellknown['provisioning'] = '/%s/persona/' % opts['instance']
106         wellknown['public-key'] = {'algorithm': 'RS',
107                                    'e': str(exponent),
108                                    'n': str(key_n)}
109         with open(os.path.join(opts['wellknown_dir'], 'browserid'), 'w') as f:
110             f.write(json.dumps(wellknown))
111
112         # Add configuration data to database
113         po = PluginObject(*self.pargs)
114         po.name = 'persona'
115         po.wipe_data()
116         po.wipe_config_values()
117         config = {'issuer domain': opts['hostname'],
118                   'idp key file': keyfile,
119                   'allowed domains': opts['hostname']}
120         po.save_plugin_config(config)
121
122         # Update global config to add login plugin
123         po.is_enabled = True
124         po.save_enabled_state()
125
126         # Fixup permissions so only the ipsilon user can read these files
127         files.fix_user_dirs(path, opts['system_user'])