0096c7aa58db14ab6366510be6627121b0fd9e2d
[cascardo/ipsilon.git] / ipsilon / providers / personaidp.py
1 # Copyright (C) 2014  Ipsilon project Contributors, for licensee see COPYING
2
3 from __future__ import absolute_import
4
5 from ipsilon.providers.common import ProviderBase
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.init_idp()
58         self.page = Persona(site, self)
59         # self.admin = AdminPage(site, self)
60
61         return self.page
62
63     def init_idp(self):
64         # Init IDP data
65         try:
66             self.key = M2Crypto.RSA.load_key(self.idp_key_file,
67                                              lambda *args: None)
68         except Exception, e:  # pylint: disable=broad-except
69             self._debug('Failed to init Persona provider: %r' % e)
70             return None
71
72     def on_enable(self):
73         super(IdpProvider, self).on_enable()
74         self.init_idp()
75
76
77 class Installer(object):
78
79     def __init__(self, *pargs):
80         self.name = 'persona'
81         self.ptype = 'provider'
82         self.pargs = pargs
83
84     def install_args(self, group):
85         group.add_argument('--persona', choices=['yes', 'no'], default='yes',
86                            help='Configure Persona Provider')
87
88     def configure(self, opts):
89         if opts['persona'] != 'yes':
90             return
91
92         # Check storage path is present or create it
93         path = os.path.join(opts['data_dir'], 'persona')
94         if not os.path.exists(path):
95             os.makedirs(path, 0700)
96
97         keyfile = os.path.join(path, 'persona.key')
98         exponent = 0x10001
99         key = M2Crypto.RSA.gen_key(2048, exponent)
100         key.save_key(keyfile, cipher=None)
101         key_n = 0
102         for c in key.n[4:]:
103             key_n = (key_n*256) + ord(c)
104         wellknown = dict()
105         wellknown['authentication'] = '/%s/persona/SignIn/' % opts['instance']
106         wellknown['provisioning'] = '/%s/persona/' % opts['instance']
107         wellknown['public-key'] = {'algorithm': 'RS',
108                                    'e': str(exponent),
109                                    'n': str(key_n)}
110         with open(os.path.join(opts['wellknown_dir'], 'browserid'), 'w') as f:
111             f.write(json.dumps(wellknown))
112
113         # Add configuration data to database
114         po = PluginObject(*self.pargs)
115         po.name = 'persona'
116         po.wipe_data()
117         po.wipe_config_values()
118         config = {'issuer domain': opts['hostname'],
119                   'idp key file': keyfile,
120                   'allowed domains': opts['hostname']}
121         po.save_plugin_config(config)
122
123         # Update global config to add login plugin
124         po.is_enabled = True
125         po.save_enabled_state()
126
127         # Fixup permissions so only the ipsilon user can read these files
128         files.fix_user_dirs(path, opts['system_user'])