2013-10-23 2 views
1

turbogears 2.2를 사용하는 것처럼 웹 응용 프로그램을 작성하는 것은 정말 강력한 프레임 워크 인 것처럼 보이지만 인증과 같은 많은 블랙 박스가 있습니다. (여기에서 repoze.who 플러그인).Turbogears 2 : 인증, 다른 테이블의 비밀번호, 업데이트시 피드백

  • 사용자 암호가

    요구 사항는 다른 테이블에

  • 감소 데이터베이스 쿼리를 저장하고 모든 요청에 ​​의해 사용자를로드 할 수 없습니다; 모든 사용자 쿼리에 의해 암호를 넣지 마십시오
  • 을 필요로 할 때 그러나, 업데이트 사용자 (예를 들어, 권한을) 해결
  • 오픈 아이디와 유사한 로그인
  • 에 대한 준비를 인증하는 동안 제어 (일시 중지 된 사용자 등)
되세요

현재 내가 model.auth에서 기본 모델을 정의

상태 - user, group, permission - 사용자에서 외부 키로 model.company. 나는 대부분의 중요한 사용자 모델을 포함하고 있습니다 :

class ApplicationAuthMetadata(TGAuthMetadata): 
    def __init__(self, sa_auth): 
     self.sa_auth = sa_auth 
    def get_user(self, identity, userid): 
     return self.sa_auth.dbsession.query(self.sa_auth.user_class).options(joinedload('company')).filter_by(user_name = userid).first() 
    def get_groups(self, identity, userid): 
     return [g.group_name for g in identity['user'].groups] 
    def get_permissions(self, identity, userid): 
     return [p.permission_name for p in identity['user'].permissions] 

그리고 root.py 컨트롤러에서 login 액션 (I 어딘가에 얻을 코드의 조각 :

여기
class User(DeclarativeBase): 
    __tablename__ = 'user' 

    id = Column(Integer, autoincrement = True, primary_key = True) 
    email = Column(String, unique = True, nullable = False) 
    name = Column(Unicode, nullable = False) 
    surname = Column(Unicode, nullable = False) 
    phone = Column(String) 
    company_id = Column(Integer, ForeignKey('company.id', use_alter = True, name = 'fk_user_company_id')) 
    company = relationship('Company', backref = 'users', foreign_keys = [company_id]) 
    _password = Column('password', Integer, ForeignKey('password.id')) 
    active = Column(Boolean, default = True) 

    _created = Column(DateTime, default = datetime.now) 
    _updated = Column(DateTime) 

    def __repr__(self): 
     return ('<User: user_name=%s>' % (self.email)) 

    def __unicode__(self): 
     return self.email 

    @property 
    def permissions(self): 
     """Return a set with all permissions granted to the user.""" 
     perms = set() 
     for g in self.groups: 
      perms = perms | set(g.permissions) 
     return perms 

    @classmethod 
    def by_email_address(cls, email): 
     """Return the user object whose email address is ``email``.""" 
     return DBSession.query(cls).filter_by(email = email).first() 

    @classmethod 
    def by_username(cls, username): 
     """Return the user object whose user name is ``username``.""" 
     return DBSession.query(cls).filter_by(_user_name = username).first() 

    def _set_password(self, passw): 
     ''' Set password. Password is saved in another table and columns references to it via ForeingKey''' 
     passwd = DBSession.query(Password).filter_by(id = self._password).first() 
     if passwd: 
      passwd.password = passw 
      DBSession.flush() 
      self._password = passwd.id 
     else: 
      p = Password() 
      p.password = passw 
      DBSession.add(p) 
      DBSession.flush() 
      self._password = p.id 

    def _get_password(self): 
     ''' Return password via ForeingKey''' 
     return DBSession.query(Password).filter_by(id = self._password).first().password 

    password = synonym('_password', descriptor = property(_get_password, _set_password)) 

    def validate_password(self, password): 
     ''' Validates password. This method has to be also in this class, because repoze.who requires it. ''' 
     hsh = sha256() 
     if isinstance(password, unicode): 
      password = password.encode('utf-8') 
     hsh.update(password + str(self.password[:64])) 
     return self.password[64:] == hsh.hexdigest() 

    # This is a hack for repoze.who.plugins.sa, because there is written in code 'user_name' as keyword 
    def _set_username(self, email): 
     self.email = email 

    def _get_username(self): 
     return self.email 

    def _get_created(self): 
     return self._created.strftime(Settings.get('datetime', 'format')) 

    def _set_created(self, dt): 
     self._created = dt 

    def _get_updated(self): 
     return self._updated.strftime(Settings.get('datetime', 'format')) 

    def _set_updated(self, dt): 
     self._updated = dt 

    created = synonym('_created', descriptor = property(_get_created, _set_created)) 
    updated = synonym('_updated', descriptor = property(_get_updated, _set_updated)) 

    user_name = synonym('email', descriptor = property(_get_username, _set_username)) 
    username = synonym('email', descriptor = property(_get_username, _set_username)) 

class Password (DeclarativeBase): 
    __tablename__ = 'password' 

    id = Column(Integer, autoincrement = True, primary_key = True) 
    _password = Column('password', Unicode(128)) 

    @classmethod 
    def _hash_password(cls, password): 
     # Make sure password is a str because we cannot hash unicode objects 
     if isinstance(password, unicode): 
      password = password.encode('utf-8') 
     salt = sha256() 
     salt.update(os.urandom(60)) 
     hsh = sha256() 
     hsh.update(password + salt.hexdigest()) 
     password = salt.hexdigest() + hsh.hexdigest() 
     # Make sure the hashed password is a unicode object at the end of the 
     # process because SQLAlchemy _wants_ unicode objects for Unicode cols 
     if not isinstance(password, unicode): 
      password = password.decode('utf-8') 
     return password 

    def _set_password(self, password): 
     """Hash ``password`` on the fly and store its hashed version.""" 
     self._password = self._hash_password(password) 

    def _get_password(self): 
     """Return the hashed version of the password.""" 
     return self._password 

    password = synonym('_password', descriptor = property(_get_password, _set_password)) 

    def validate_password(self, password): 
     """ 
     Check the password against existing credentials. 

     :param password: the password that was provided by the user to 
      try and authenticate. This is the clear text version that we will 
      need to match against the hashed one in the database. 
     :type password: unicode object. 
     :return: Whether the password is valid. 
     :rtype: bool 

     """ 
     hsh = sha256() 
     if isinstance(password, unicode): 
      password = password.encode('utf-8') 
     hsh.update(password + str(self.password[:64])) 
     return self.password[64:] == hsh.hexdigest() 

현재 내가 app_cfg.py에서 데이터를 가져 오는 방법 상태) :

''' AUTHORIZATION ''' 
@expose('mizuno.templates.login') 
def login(self, came_from = lurl('/')): 
    '''Start the user login.''' 
    if request.identity and request.identity['user']: 
     redirect('/tickets') 
    login_counter = request.environ.get('repoze.who.logins', 0) 
    if login_counter > 0: 
     flash(_('Wrong credentials'), 'warning') 
    return { 
     'page': 'login', 
     'login_counter': str(login_counter), 
     'came_from': came_from 
    } 

그러나이 모든 요청에 ​​의해 사용자 정보뿐만 아니라 그것으로 사용자 암호를 받고있다 :

SELECT "user".password AS user_password, "user".id AS user_id, "user".email AS user_email, 
    "user".name AS user_name, "user".surname AS user_surname, "user".phone AS user_phone, 
    "user".company_id AS user_company_id, "user".active AS user_active, "user"._created AS user__created, 
    "user"._updated AS user__updated, company_1.ic AS company_1_ic, 
    company_1.id AS company_1_id, company_1.name AS company_1_name, company_1.dic AS company_1_dic, 
    company_1.address AS company_1_address, company_1.email AS company_1_email, 
    company_1.is_supplier AS company_1_is_supplier, company_1.supplier_id AS company_1_supplier_id, 
    company_1.active AS company_1_active, company_1.creator_id AS company_1_creator_id, 
    company_1.updator_id AS company_1_updator_id, company_1._created AS company_1__created, 
    company_1._updated AS company_1__updated 
FROM "user" LEFT OUTER JOIN company AS company_1 ON company_1.id = "user".company_id 
WHERE "user".email = %(email_1)s 
LIMIT %(param_1)s 

마지막 질문

, 어떻게 Turbogears에서 인증을 이해하고 깨끗한 방법으로 모든 요구 사항을 fullfill에에 해결하는 방법을 가르쳐주세요? 미리 감사드립니다. 업그레이드가 불가능하다으로

UPDATE는

, TG 2.2에 대한 솔루션을 제공하십시오.

답변

1

은 당신이 최신 버전은 사용자 이름과 암호 유효 사용자 정의 검사를 제공하기 위해 쉽게 ApplicationAuthMetadataauthenticate 방법을 지원, TurboGears 2.3으로 업그레이드하는 것이 좋습니다. 당신은 TurboGears를 업그레이드 할 수없는 경우

class ApplicationAuthMetadata(TGAuthMetadata): 
    def __init__(self, sa_auth): 
     self.sa_auth = sa_auth 

    def authenticate(self, environ, identity): 
     user = self.sa_auth.dbsession.query(self.sa_auth.user_class).filter_by(user_name=identity['login']).first() 
     if user and user.validate_password(identity['password']): 
      return identity['login'] 

    # Here are the get_user, get_groups and get_permissions 

당신이 좀 더 복잡 사용자 정의 repoze.who 인증을 구현해야합니다

표준 ApplicationAuthMetadata.authenticate 구현처럼 보인다.당신은 그것에 대한 일부 문서를 찾을 수 있습니다 http://turbogears.readthedocs.org/en/latest/turbogears/authentication.html

+0

답장을 보내 주셔서 감사합니다, 그것은 그것을 찾을 수없는 버전 2.2에 인증 방법이 없다는 것을 의미합니까? 2.2에서 _AuthenticationForgerPlugin 클래스를 사용할 수 있습니까? 나는 업그레이드에 대해 확신하지 못합니다. 기어 박스는 문제가 아니지만 sqlalchemy 0.8을 사용하여 sqlalchemy-migrate를 사용하고 있습니다. (문제는 postgres를 사용하여) 모든 문제가 해결되었습니다. 어쨌든, 아직 모릅니다. 2.2에서 2.2에서도 적절한 문서가 누락되어서 어떻게 작동하는지 ... repoze. TG에서 사용하는 방식에 대해 많이 말하지 않은 사람 ... 어떤 생각입니까? – tomis

+0

기어 박스는 여전히 sqlalchemy-migrate를'sqla-migrate' 명령을 통해 사용할 수 있습니다. 업그레이드를 시도 할 것을 제안합니다. 필요한 것을 훨씬 쉽게 할 수 있습니다. 2.2로 고착되어 있다면 사용자 정의 인증 자, 식별자 등을 추가 할 수있는 구성 옵션이 있습니다. http://turbogears.readthedocs.org/en/latest/turbogears/authentication.html#adavanced-customizations에 나열되어 있습니다. 귀하의 경우 repoze.who 문서 http : //를 볼 수있는 맞춤형 인증 기가 필요합니다. docs.repoze.org/who/2.0/plugins.html#writing-an-authenticator-plugin – amol

+0

감사합니다.이 내용을 확인하고 나중에이 작은 페이지로 돌아갑니다 ... – tomis

관련 문제