3 # Copyright (C) 2014 Simo Sorce <simo@redhat.com>
5 # see file 'COPYING' for use and warranty information
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from ipsilon.providers.common import ProviderException
26 'email': lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL,
27 'encrypted': lasso.SAML2_NAME_IDENTIFIER_FORMAT_ENCRYPTED,
28 'entity': lasso.SAML2_NAME_IDENTIFIER_FORMAT_ENTITY,
29 'kerberos': lasso.SAML2_NAME_IDENTIFIER_FORMAT_KERBEROS,
30 'persistent': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
31 'transient': lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT,
32 'unspecified': lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED,
33 'windows': lasso.SAML2_NAME_IDENTIFIER_FORMAT_WINDOWS,
34 'x509': lasso.SAML2_NAME_IDENTIFIER_FORMAT_X509,
38 class InvalidProviderId(ProviderException):
40 def __init__(self, code):
41 message = 'Invalid Provider ID: %s' % code
42 super(InvalidProviderId, self).__init__(message)
46 class NameIdNotAllowed(Exception):
49 message = 'The specified Name ID is not allowed'
50 super(NameIdNotAllowed, self).__init__(message)
51 self.message = message
54 return repr(self.message)
57 class ServiceProvider(object):
59 def __init__(self, config, provider_id):
61 data = self.cfg.get_data(name='id', value=provider_id)
63 raise InvalidProviderId('multiple matches')
64 idval = data.keys()[0]
65 data = self.cfg.get_data(idval=idval)
66 self._properties = data[idval]
67 self._staging = dict()
70 def provider_id(self):
71 return self._properties['id']
75 return self._properties['name']
78 def name(self, value):
79 self._staging['name'] = value
83 if 'owner' in self._properties:
84 return self._properties['owner']
89 def owner(self, value):
90 self._staging['owner'] = value
93 def allowed_nameids(self):
94 if 'allowed nameids' in self._properties:
95 allowed = self._properties['allowed nameids']
96 return [x.strip() for x in allowed.split(',')]
98 return self.cfg.default_allowed_nameids
100 @allowed_nameids.setter
101 def allowed_nameids(self, value):
102 if type(value) is not list:
103 raise ValueError("Must be a list")
104 self._staging['allowed nameids'] = ','.join(value)
107 def default_nameid(self):
108 if 'default nameid' in self._properties:
109 return self._properties['default nameid']
111 return self.cfg.default_nameid
113 @default_nameid.setter
114 def default_nameid(self, value):
115 self._staging['default nameid'] = value
117 def save_properties(self):
118 data = self.cfg.get_data(name='id', value=self.provider_id)
120 raise InvalidProviderId('Could not find SP data')
121 idval = data.keys()[0]
123 data[idval] = self._staging
124 self.cfg.save_data(data)
125 data = self.cfg.get_data(idval=idval)
126 self._properties = data[idval]
127 self._staging = dict()
129 def get_valid_nameid(self, nip):
130 self._debug('Requested NameId [%s]' % (nip.format,))
131 if nip.format is None:
132 return NAMEID_MAP[self.default_nameid]
133 elif nip.format == lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED:
134 return NAMEID_MAP[self.default_nameid]
136 allowed = self.allowed_nameids
137 self._debug('Allowed NameIds %s' % (repr(allowed)))
138 for nameid in allowed:
139 if nip.format == NAMEID_MAP[nameid]:
141 raise NameIdNotAllowed(nip.format)
143 def permanently_delete(self):
144 data = self.cfg.get_data(name='id', value=self.provider_id)
146 raise InvalidProviderId('Could not find SP data')
147 idval = data.keys()[0]
148 self.cfg.del_datum(idval)
150 def _debug(self, fact):
151 if cherrypy.config.get('debug', False):
154 def normalize_username(self, username):
155 if 'strip domain' in self._properties:
156 return username.split('@', 1)[0]
160 class ServiceProviderCreator(object):
162 def __init__(self, config):
165 def create_from_buffer(self, name, metabuf):
166 '''Test and add data'''
168 test = lasso.Server()
169 test.addProviderFromBuffer(lasso.PROVIDER_ROLE_SP, metabuf)
170 newsps = test.get_providers()
172 raise InvalidProviderId("Metadata must contain one Provider")
174 spid = newsps.keys()[0]
175 data = self.cfg.get_data(name='id', value=spid)
177 raise InvalidProviderId("Provider Already Exists")
178 datum = {'id': spid, 'name': name, 'type': 'SP', 'metadata': metabuf}
179 self.cfg.new_datum(datum)
181 data = self.cfg.get_data(name='id', value=spid)
183 raise InvalidProviderId("Internal Error")
184 idval = data.keys()[0]
185 data = self.cfg.get_data(idval=idval)
187 self.cfg.idp.add_provider(sp)
189 return ServiceProvider(self.cfg, spid)
192 class IdentityProvider(object):
193 def __init__(self, config):
194 self.server = lasso.Server(config.idp_metadata_file,
197 config.idp_certificate_file)
198 self.server.role = lasso.PROVIDER_ROLE_IDP
200 def add_provider(self, sp):
201 self.server.addProviderFromBuffer(lasso.PROVIDER_ROLE_SP,
203 self._debug('Added SP %s' % sp['name'])
205 def get_login_handler(self, dump=None):
207 return lasso.Login.newFromDump(self.server, dump)
209 return lasso.Login(self.server)
211 def get_providers(self):
212 return self.server.get_providers()
214 def _debug(self, fact):
215 if cherrypy.config.get('debug', False):