| | 177 | {{if elixir}} |
|---|
| | 178 | |
|---|
| | 179 | from sqlalchemy.orm import scoped_session, sessionmaker |
|---|
| | 180 | from datetime import datetime |
|---|
| | 181 | import elixir |
|---|
| | 182 | from elixir import Entity, Field |
|---|
| | 183 | from elixir import DateTime, Unicode |
|---|
| | 184 | from elixir import using_options |
|---|
| | 185 | from elixir import ManyToMany |
|---|
| | 186 | |
|---|
| | 187 | from tg import config |
|---|
| | 188 | |
|---|
| | 189 | # Global session manager. DBSession() returns the session object |
|---|
| | 190 | # appropriate for the current web request. |
|---|
| | 191 | maker = sessionmaker(autoflush=True, autocommit=False) |
|---|
| | 192 | DBSession = scoped_session(maker) |
|---|
| | 193 | |
|---|
| | 194 | metadata = elixir.metadata |
|---|
| | 195 | |
|---|
| | 196 | metadata.bind = "sqlite:///:memory:" |
|---|
| | 197 | metadata.bind.echo = True |
|---|
| | 198 | |
|---|
| | 199 | class User(Entity): |
|---|
| | 200 | """Reasonably basic User definition. Probably would want additional |
|---|
| | 201 | attributes. |
|---|
| | 202 | """ |
|---|
| | 203 | using_options(tablename="tg_user", auto_primarykey="user_id") |
|---|
| | 204 | |
|---|
| | 205 | user_name = Field(Unicode(16), required=True, unique=True) |
|---|
| | 206 | |
|---|
| | 207 | email_address = Field(Unicode(255), required=True, unique=True) |
|---|
| | 208 | |
|---|
| | 209 | display_name = Field(Unicode(255)) |
|---|
| | 210 | |
|---|
| | 211 | created = Field(DateTime, default=datetime.now) |
|---|
| | 212 | |
|---|
| | 213 | _password = Field(Unicode(40), colname="password", required=True) |
|---|
| | 214 | |
|---|
| | 215 | groups = ManyToMany( |
|---|
| | 216 | "Group", |
|---|
| | 217 | inverse="users", |
|---|
| | 218 | tablename="tg_user_group", |
|---|
| | 219 | local_colname="group_id", |
|---|
| | 220 | remote_colname="user_id", |
|---|
| | 221 | ) |
|---|
| | 222 | |
|---|
| | 223 | def __repr__(self): |
|---|
| | 224 | return '<User: email="%s", display name="%s">' % ( |
|---|
| | 225 | self.email_address, self.display_name) |
|---|
| | 226 | |
|---|
| | 227 | |
|---|
| | 228 | |
|---|
| | 229 | @property |
|---|
| | 230 | def permissions(self): |
|---|
| | 231 | perms = set() |
|---|
| | 232 | for g in self.groups: |
|---|
| | 233 | perms = perms | set(g.permissions) |
|---|
| | 234 | return perms |
|---|
| | 235 | |
|---|
| | 236 | @classmethod |
|---|
| | 237 | def by_email_address(cls, email): |
|---|
| | 238 | """A class method that can be used to search users |
|---|
| | 239 | based on their email addresses since it is unique. |
|---|
| | 240 | """ |
|---|
| | 241 | return DBSession.query(cls).filter(cls.email_address==email).first() |
|---|
| | 242 | |
|---|
| | 243 | @classmethod |
|---|
| | 244 | def by_user_name(cls, username): |
|---|
| | 245 | """A class method that permits to search users |
|---|
| | 246 | based on their user_name attribute. |
|---|
| | 247 | """ |
|---|
| | 248 | return DBSession.query(cls).filter(cls.user_name==username).first() |
|---|
| | 249 | |
|---|
| | 250 | |
|---|
| | 251 | def _set_password(self, password): |
|---|
| | 252 | """encrypts password on the fly using the encryption |
|---|
| | 253 | algo defined in the configuration |
|---|
| | 254 | """ |
|---|
| | 255 | algorithm = config.get('authorize.hashmethod', None) |
|---|
| | 256 | self._password = self.__encrypt_password(algorithm, password) |
|---|
| | 257 | |
|---|
| | 258 | def _get_password(self): |
|---|
| | 259 | """returns password |
|---|
| | 260 | """ |
|---|
| | 261 | return self._password |
|---|
| | 262 | |
|---|
| | 263 | password = descriptor=property(_get_password, _set_password) |
|---|
| | 264 | |
|---|
| | 265 | def __encrypt_password(self, algorithm, password): |
|---|
| | 266 | """Hash the given password with the specified algorithm. Valid values |
|---|
| | 267 | for algorithm are 'md5' and 'sha1'. All other algorithm values will |
|---|
| | 268 | be essentially a no-op.""" |
|---|
| | 269 | hashed_password = password |
|---|
| | 270 | |
|---|
| | 271 | if isinstance(password, unicode): |
|---|
| | 272 | password_8bit = password.encode('UTF-8') |
|---|
| | 273 | |
|---|
| | 274 | else: |
|---|
| | 275 | password_8bit = password |
|---|
| | 276 | |
|---|
| | 277 | if "md5" == algorithm: |
|---|
| | 278 | hashed_password = md5.new(password_8bit).hexdigest() |
|---|
| | 279 | |
|---|
| | 280 | elif "sha1" == algorithm: |
|---|
| | 281 | hashed_password = sha.new(password_8bit).hexdigest() |
|---|
| | 282 | |
|---|
| | 283 | # TODO: re-add the possibility to provide own hasing algo |
|---|
| | 284 | # here... just get the real config... |
|---|
| | 285 | |
|---|
| | 286 | #elif "custom" == algorithm: |
|---|
| | 287 | # custom_encryption_path = turbogears.config.get( |
|---|
| | 288 | # "identity.custom_encryption", None ) |
|---|
| | 289 | # |
|---|
| | 290 | # if custom_encryption_path: |
|---|
| | 291 | # custom_encryption = turbogears.util.load_class( |
|---|
| | 292 | # custom_encryption_path) |
|---|
| | 293 | |
|---|
| | 294 | # if custom_encryption: |
|---|
| | 295 | # hashed_password = custom_encryption(password_8bit) |
|---|
| | 296 | |
|---|
| | 297 | # make sure the hased password is an UTF-8 object at the end of the |
|---|
| | 298 | # process because SQLAlchemy _wants_ a unicode object for Unicode columns |
|---|
| | 299 | if not isinstance(hashed_password, unicode): |
|---|
| | 300 | hashed_password = hashed_password.decode('UTF-8') |
|---|
| | 301 | |
|---|
| | 302 | return hashed_password |
|---|
| | 303 | |
|---|
| | 304 | def validate_password(self, password): |
|---|
| | 305 | """Check the password against existing credentials. |
|---|
| | 306 | """ |
|---|
| | 307 | identity = config.get('identity', None) |
|---|
| | 308 | if identity is None: |
|---|
| | 309 | return password |
|---|
| | 310 | algorithm = identity.get('password_encryption_method', None) |
|---|
| | 311 | return self.password == self.__encrypt_password(algorithm, password) |
|---|
| | 312 | |
|---|
| | 313 | |
|---|
| | 314 | class Group(Entity): |
|---|
| | 315 | """An ultra-simple group definition. |
|---|
| | 316 | """ |
|---|
| | 317 | using_options(tablename="tg_group", auto_primarykey="group_id") |
|---|
| | 318 | |
|---|
| | 319 | group_name = Field(Unicode(16), unique=True) |
|---|
| | 320 | |
|---|
| | 321 | display_name = Field(Unicode(255)) |
|---|
| | 322 | |
|---|
| | 323 | created = Field(DateTime, default=datetime.now) |
|---|
| | 324 | |
|---|
| | 325 | users = ManyToMany("User") |
|---|
| | 326 | |
|---|
| | 327 | permissions = ManyToMany( |
|---|
| | 328 | "Permission", |
|---|
| | 329 | inverse="groups", |
|---|
| | 330 | tablename="tg_group_permission", |
|---|
| | 331 | local_colname="group_id", |
|---|
| | 332 | remote_colname="permission_id", |
|---|
| | 333 | ) |
|---|
| | 334 | |
|---|
| | 335 | def __repr__(self): |
|---|
| | 336 | return '<Group: name=%s>' % self.group_name |
|---|
| | 337 | |
|---|
| | 338 | |
|---|
| | 339 | class Permission(Entity): |
|---|
| | 340 | """A relationship that determines what each Group can do |
|---|
| | 341 | """ |
|---|
| | 342 | using_options(tablename="tg_permission", auto_primarykey="permission_id") |
|---|
| | 343 | |
|---|
| | 344 | permission_name = Field(Unicode(16), unique=True) |
|---|
| | 345 | |
|---|
| | 346 | description = Field(Unicode(255)) |
|---|
| | 347 | |
|---|
| | 348 | groups = ManyToMany("Group") |
|---|
| | 349 | |
|---|
| | 350 | {{endif}} |
|---|
| | 351 | {{endif}} |
|---|