Package camelot :: Package camelot :: Package model :: Module authentication
[hide private]
[frames] | no frames]

Source Code for Module camelot.camelot.model.authentication

  1  #  ============================================================================
 
  2  #
 
  3  #  Copyright (C) 2007-2008 Conceptive Engineering bvba. All rights reserved.
 
  4  #  www.conceptive.be / project-camelot@conceptive.be
 
  5  #
 
  6  #  This file is part of the Camelot Library.
 
  7  #
 
  8  #  This file may be used under the terms of the GNU General Public
 
  9  #  License version 2.0 as published by the Free Software Foundation
 
 10  #  and appearing in the file LICENSE.GPL included in the packaging of
 
 11  #  this file.  Please review the following information to ensure GNU
 
 12  #  General Public Licensing requirements will be met:
 
 13  #  http://www.trolltech.com/products/qt/opensource.html
 
 14  #
 
 15  #  If you are unsure which license is appropriate for your use, please
 
 16  #  review the following information:
 
 17  #  http://www.trolltech.com/products/qt/licensing.html or contact
 
 18  #  project-camelot@conceptive.be.
 
 19  #
 
 20  #  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
 21  #  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
 22  #
 
 23  #  For use of this library in commercial applications, please contact
 
 24  #  project-camelot@conceptive.be
 
 25  #
 
 26  #  ============================================================================
 
 27  """Set of classes to store persons, organizations, relationships and
 
 28  permissions
 
 29  
 
 30  These structures are modeled like described in 'The Data Model Resource Book'
 
 31  by Len Silverston, Chapter 2
 
 32  """ 
 33  
 
 34  import camelot 
 35  import camelot.types 
 36  
 
 37  from camelot.model import * 
 38  from camelot.model.synchronization import * 
 39  
 
 40  __metadata__ = metadata 
 41  
 
 42  from camelot.view.elixir_admin import EntityAdmin 
 43  from camelot.view.forms import Form, TabForm, VBoxForm, HBoxForm, WidgetOnlyForm 
 44  import datetime 
 45  
 
 46  _current_person_ = None 
47 48 -def end_of_times():
49 return datetime.date(year=2400, month=12, day=31)
50
51 -def getCurrentPerson():
52 """Get the currently logged in person""" 53 global _current_person_ 54 if not _current_person_: 55 import getpass 56 _current_person_ = Person.getOrCreatePerson(unicode(getpass.getuser())) 57 return _current_person_
58
59 -def updateLastLogin():
60 """Update the last login of the current person to now""" 61 from elixir import session 62 person = getCurrentPerson() 63 person.last_login = datetime.datetime.now() 64 session.flush([person])
65
66 -class PartyRelationship(Entity):
67 using_options(tablename='party_relationship') 68 from_date = Field(Date(), default=datetime.date.today, required=True, index=True) 69 thru_date = Field(Date(), default=end_of_times, required=True, index=True) 70 comment = Field(Text) 71 is_synchronized('synchronized', lazy=True)
72
73 -class EmployerEmployee(PartyRelationship):
74 """Relation from employer to employee""" 75 using_options(tablename='party_relationship_empl', inheritance='multi') 76 established_from = ManyToOne('Organization', required=True, ondelete='cascade', onupdate='cascade') 77 established_to = ManyToOne('Person', required=True, ondelete='cascade', onupdate='cascade') 78
79 - class EmployeeAdmin(EntityAdmin):
80 name = 'Employees' 81 list_display = ['established_to', 'from_date', 'thru_date'] 82 fields = ['established_to', 'comment', 'from_date', 'thru_date'] 83 field_attributes = {'established_to':{'name':'Name'}}
84
85 - class EmployerAdmin(EntityAdmin):
86 name = 'Employers' 87 list_display = ['established_from', 'from_date', 'thru_date'] 88 fields = ['established_from', 'comment', 'from_date', 'thru_date'] 89 field_attributes = {'established_from':{'name':'Name'}}
90
91 -class DirectedDirector(PartyRelationship):
92 """Relation from a directed organization to a director""" 93 using_options(tablename='party_relationship_dir', inheritance='multi') 94 established_from = ManyToOne('Organization', required=True, ondelete='cascade', onupdate='cascade') 95 established_to = ManyToOne('Person', required=True, ondelete='cascade', onupdate='cascade') 96 title = Field(Unicode(256)) 97 representing = OneToMany('RepresentedRepresentor', inverse='established_to') 98
99 - class DirectorAdmin(EntityAdmin):
100 name = 'Directors' 101 list_display = ['established_to', 'from_date', 'thru_date'] 102 fields = ['established_to', 'title', 'from_date', 'thru_date', 'representing', 'comment'] 103 field_attributes = {'established_to':{'name':'Name'}}
104
105 - class DirectedAdmin(EntityAdmin):
106 name = 'Directed organizations' 107 list_display = ['established_from', 'from_date', 'thru_date'] 108 fields = ['established_from', 'from_date', 'thru_date', 'representing', 'comment'] 109 field_attributes = {'established_from':{'name':'Name'}}
110
111 -class RepresentedRepresentor(Entity):
112 """Relation from a represented party to the director representing the party""" 113 using_options(tablename='party_representor') 114 from_date = Field(Date(), default=datetime.date.today, required=True, index=True) 115 thru_date = Field(Date(), default=end_of_times, required=True, index=True) 116 comment = Field(Text) 117 established_from = ManyToOne('Party', required=True, ondelete='cascade', onupdate='cascade') 118 established_to = ManyToOne('DirectedDirector', required=True, ondelete='cascade', onupdate='cascade') 119
120 - class Admin(EntityAdmin):
121 name = 'Representing' 122 list_display = ['established_from', 'from_date', 'thru_date', 'comment'] 123 field_attributes = {'established_from':{'name':'Name'}}
124
125 -class SupplierCustomer(PartyRelationship):
126 """Relation from supplier to customer""" 127 using_options(tablename='party_relationship_suppl', inheritance='multi') 128 established_from = ManyToOne('Party', required=True, ondelete='cascade', onupdate='cascade') 129 established_to = ManyToOne('Party', required=True, ondelete='cascade', onupdate='cascade') 130
131 - class CustomerAdmin(EntityAdmin):
132 name = 'Customers' 133 list_display = ['established_to',] 134 fields = ['established_to', 'comment', 'from_date', 'thru_date'] 135 field_attributes = {'established_to':{'name':'Name'}}
136
137 - class SupplierAdmin(EntityAdmin):
138 name = 'Suppliers' 139 list_display = ['established_from',] 140 fields = ['established_from', 'comment', 'from_date', 'thru_date'] 141 field_attributes = {'established_from':{'name':'Name'}}
142
143 -class SharedShareholder(PartyRelationship):
144 """Relation from a shared organization to a shareholder""" 145 using_options(tablename='party_relationship_shares', inheritance='multi') 146 established_from = ManyToOne('Organization', required=True, ondelete='cascade', onupdate='cascade') 147 established_to = ManyToOne('Party', required=True, ondelete='cascade', onupdate='cascade') 148 shares = Field(Integer()) 149
150 - class ShareholderAdmin(EntityAdmin):
151 name = 'Shareholders' 152 list_display = ['established_to', 'shares', 'from_date', 'thru_date'] 153 fields = ['established_to', 'shares', 'from_date', 'thru_date', 'comment'] 154 field_attributes = {'established_to':{'name':'Shareholder name'}}
155
156 - class SharedAdmin(EntityAdmin):
157 name = 'Shares' 158 list_display = ['established_from', 'shares', 'from_date', 'thru_date'] 159 fields = ['established_from', 'shares', 'from_date', 'thru_date', 'comment'] 160 field_attributes = {'established_from':{'name':'Name'}}
161
162 -class Party(Entity):
163 """Base class for persons and organizations. Use this base class to refer to either persons or 164 organisations in building authentication systems, contact management or CRM""" 165 using_options(tablename='party') 166 is_synchronized('synchronized', lazy=True) 167 addresses = OneToMany('PartyAddress', lazy=True) 168 contact_mechanisms = OneToMany('PartyContactMechanism', lazy=True) 169 170 @property
171 - def name(self):
172 return unicode(self)
173
174 - class Admin(EntityAdmin):
175 name = 'Parties' 176 list_display = ['name'] 177 fields = ['suppliers', 'customers', 'addresses'] 178 field_attributes = dict(suppliers={'admin':SupplierCustomer.SupplierAdmin}, 179 customers={'admin':SupplierCustomer.CustomerAdmin}, 180 employers={'admin':EmployerEmployee.EmployerAdmin}, 181 employees={'admin':EmployerEmployee.EmployeeAdmin}, 182 directed_organizations={'admin':DirectedDirector.DirectedAdmin}, 183 directors={'admin':DirectedDirector.DirectorAdmin}, 184 shares={'admin':SharedShareholder.SharedAdmin}, 185 shareholders={'admin':SharedShareholder.ShareholderAdmin}, 186 sex=dict(choices=lambda obj:[(u'M',u'Male'), (u'F',u'Female')],), 187 )
188
189 -class Organization(Party):
190 """An organization represents any internal or external organization. Organizations can include 191 businesses and groups of individuals""" 192 using_options(tablename='organization', inheritance='multi') 193 name = Field(Unicode(50), required=True, index=True) 194 logo = Field(camelot.types.Image(upload_to='organization-logo'), deferred=True) 195 tax_id = Field(Unicode(20)) 196 directors = OneToMany('DirectedDirector', inverse='established_from') 197 employees = OneToMany('EmployerEmployee', inverse='established_from') 198 suppliers = OneToMany('SupplierCustomer', inverse='established_to') 199 customers = OneToMany('SupplierCustomer', inverse='established_from') 200 shareholders = OneToMany('SharedShareholder', inverse='established_from') 201 shares = OneToMany('SharedShareholder', inverse='established_to') 202
203 - def __unicode__(self):
204 return self.name
205 206 @property
207 - def number_of_shares_issued(self):
208 return sum((shareholder.shares for shareholder in self.shareholders), 0)
209
210 - class Admin(Party.Admin):
211 name = 'Organizations' 212 section = 'relations' 213 list_display = ['name', 'tax_id',] 214 form = TabForm([('Basic', Form(['name', 'tax_id', 'addresses', 'contact_mechanisms'])), 215 ('Employment', Form(['employees'])), 216 ('Customers', Form(['customers'])), 217 ('Suppliers', Form(['suppliers'])), 218 ('Corporate', Form(['directors', 'shareholders', 'shares'])), 219 ('Branding', Form(['logo'])), ])
220
221 -class Person(Party):
222 """Person represents natural persons, these can be given access to the system 223 and as such require a username. 224 225 Username is required, other fields are optional, there is no password because 226 authentication is supposed to happen through the operating system services or 227 other. 228 """ 229 using_options(tablename='person', inheritance='multi') 230 username = Field(Unicode(40), required=True, index=True, unique=True) 231 first_name = Field(Unicode(40)) 232 last_name = Field(Unicode(40)) 233 middle_name = Field(Unicode(40)) 234 personal_title = Field(Unicode(10)) 235 suffix = Field(Unicode(3)) 236 sex = Field(Unicode(1), default=u'M') 237 birthdate = Field(Date()) 238 martial_status = Field(Unicode(1)) 239 social_security_number = Field(Unicode(12)) 240 passport_number = Field(Unicode(20)) 241 passport_expiry_date = Field(Date()) 242 is_staff = Field(Boolean, default=False, index=True) 243 is_active = Field(Boolean, default=True, index=True) 244 is_superuser = Field(Boolean, default=False, index=True) 245 last_login = Field(DateTime(), default=datetime.datetime.now) 246 date_joined = Field(DateTime(), default=datetime.datetime.now) 247 picture = Field(camelot.types.Image(upload_to='person-pictures'), deferred=True) 248 comment = Field(Text) 249 employers = OneToMany('EmployerEmployee', inverse='established_to') 250 directed_organizations = OneToMany('DirectedDirector', inverse='established_to') 251 shares = OneToMany('SharedShareholder', inverse='established_to') 252 253 @property
254 - def name(self):
255 if self.last_name and self.first_name: 256 return u'%s %s'%(self.last_name, self.first_name) 257 else: 258 return self.username
259
260 - def __unicode__(self):
261 return self.name
262 263 @classmethod
264 - def getOrCreatePerson(cls, username):
265 person = cls.query.filter_by(username=username).first() 266 if not person: 267 person = cls(username=username) 268 from elixir import session 269 session.flush([person]) 270 return person
271
272 - class Admin(Party.Admin):
273 name = 'Persons' 274 section = 'relations' 275 list_display = ['username', 'first_name', 'last_name', ] 276 list_filter = ['is_active', 'is_staff', 'is_superuser'] 277 form = TabForm([('Basic', Form([HBoxForm([Form(['username', 'first_name', 'last_name', 'sex']), 278 Form(['is_staff', 'is_active', 'is_superuser',]), 279 Form(['picture',]), 280 ]), 281 'contact_mechanisms', 'comment',], scrollbars=True)), 282 ('Official', Form(['birthdate', 'social_security_number', 'passport_number','passport_expiry_date','addresses',], scrollbars=True)), 283 ('Work', Form(['employers', 'directed_organizations', 'shares'], scrollbars=True)) 284 ])
285
286 -class GeographicBoundary(Entity):
287 using_options(tablename='geographic_boundary') 288 code = Field(Unicode(10)) 289 name = Field(Unicode(40), required=True) 290
291 - def __unicode__(self):
292 return u'%s %s'%(self.code, self.name)
293
294 -class Country(GeographicBoundary):
295 using_options(tablename='geographic_boundary_country', inheritance='multi') 296 297 @classmethod
298 - def getOrCreate(cls, code, name):
299 country = Country.query.filter_by(code=code).first() 300 if not country: 301 from elixir import session 302 country = Country(code=code, name=name) 303 session.flush([country]) 304 return country
305
306 - class Admin(EntityAdmin):
307 name = 'Countries' 308 list_display = ['name', 'code']
309
310 -class City(GeographicBoundary):
311 using_options(tablename='geographic_boundary_city', inheritance='multi') 312 country = ManyToOne('Country', required=True, ondelete='cascade', onupdate='cascade') 313 314 @classmethod
315 - def getOrCreate(cls, country, code, name):
316 city = City.query.filter_by(code=code, country=country).first() 317 if not city: 318 from elixir import session 319 city = City(code=code, name=name, country=country) 320 session.flush([city]) 321 return city
322
323 - class Admin(EntityAdmin):
324 name = 'Cities' 325 list_display = ['code', 'name', 'country']
326
327 -class Address(Entity):
328 using_options(tablename='address') 329 street1 = Field(Unicode(128), required=True) 330 street2 = Field(Unicode(128)) 331 city = ManyToOne('City', required=True, ondelete='cascade', onupdate='cascade') 332 is_synchronized('synchronized', lazy=True) 333
334 - def __unicode__(self):
335 return u'%s, %s'%(self.street1, self.city)
336
337 - def showMap(self):
338 from PyQt4 import QtGui, QtCore 339 QtGui.QDesktopServices.openUrl (QtCore.QUrl('http://www.google.be/maps?f=q&source=s_q&geocode=%s&q=%s+%s'%(self.city.country.code, self.street1, self.city.name)))
340
341 - class Admin(EntityAdmin):
342 name = 'Addresses' 343 list_display = ['street1', 'street2', 'city'] 344 form_actions = [('Show map',lambda address:address.showMap())]
345
346 -class PartyAddressRoleType(Entity):
347 using_options(tablename='party_address_role_type') 348 code = Field(Unicode(10)) 349 description = Field(Unicode(40)) 350
351 - class Admin(EntityAdmin):
352 name = 'Address role type' 353 list_display = ['code', 'description']
354
355 -class PartyAddress(Entity):
356 using_options(tablename='party_address') 357 party = ManyToOne('Party', required=True, ondelete='cascade', onupdate='cascade') 358 address = ManyToOne('Address', required=True, ondelete='cascade', onupdate='cascade') 359 from_date = Field(Date(), default=datetime.date.today, required=True, index=True) 360 thru_date = Field(Date(), default=end_of_times, required=True, index=True) 361 comment = Field(Unicode(256)) 362
363 - def __unicode__(self):
364 return '%s : %s'%(unicode(self.party), unicode(self.address))
365
366 - def showMap(self):
367 if self.address: 368 self.address.showMap()
369
370 - class Admin(EntityAdmin):
371 name = 'Address' 372 list_display = ['address', 'comment'] 373 fields = ['address', 'comment', 'from_date', 'thru_date'] 374 form_actions = [('Show map',lambda address:address.showMap())]
375
376 -class ContactMechanism(Entity):
377 using_options(tablename='contact_mechanism') 378 mechanism = Field(camelot.types.VirtualAddress(256), required=True) 379 party_address = ManyToOne('PartyAddress', ondelete='set null', onupdate='cascade') 380
381 - def __unicode__(self):
382 return self.mechanism[1]
383
384 - class Admin(EntityAdmin):
385 name = 'Contact mechanism' 386 list_display = ['mechanism'] 387 form = Form(['mechanism', 'party_address'])
388
389 -class PartyContactMechanism(Entity):
390 using_options(tablename='party_contact_mechanism') 391 party = ManyToOne('Party', required=True, ondelete='cascade', onupdate='cascade') 392 contact_mechanism = ManyToOne('ContactMechanism', required=True, ondelete='cascade', onupdate='cascade') 393 from_date = Field(Date(), default=datetime.date.today, required=True, index=True) 394 thru_date = Field(Date(), default=end_of_times, index=True) 395 comment = Field(Unicode(256)) 396
397 - def __unicode__(self):
398 return unicode(self.contact_mechanism)
399
400 - class Admin(EntityAdmin):
401 name = 'Party contact mechanisms' 402 list_display = ['contact_mechanism', 'comment', 'from_date',] 403 form = Form(['contact_mechanism', 'comment', 'from_date', 'thru_date',])
404