# -*- coding: utf-8 -*-
"""
web2ldap plugin classes for attributes defined in draft-behera-ldap-password-policy
"""

import time,datetime,pyweblib.forms,utctime,w2lapp.gui,w2lapp.searchform

from calendar import timegm

from w2lapp.schema.syntaxes import SelectList,DynamicDNSelectList,Timespan,GeneralizedTime,syntax_registry
from w2lapp.schema.plugins.quirks import UserPassword
from ldap.filter import escape_filter_chars
from ldap import LDAPError

class PwdCheckQuality(SelectList):
  oid = 'PwdCheckQuality-oid'
  desc = 'Password quality checking enforced'
  attr_value_dict = {
    u'0':u'quality checking not be enforced',
    u'1':u'quality checking enforced, accepting un-checkable passwords',
    u'2':u'quality checking always enforced',
  }

syntax_registry.registerAttrType(
  PwdCheckQuality.oid,[
    '1.3.6.1.4.1.42.2.27.8.1.5', # pwdCheckQuality (see draft-behera-ldap-password-policy)
  ]
)


class PwdAttribute(SelectList):
  oid = 'PwdAttribute-oid'
  desc = 'Password attribute'
  attr_value_dict = {
    u'2.5.4.35':u'userPassword',
  }

  def _validate(self,attrValue):
    return not attrValue or attrValue in ('2.5.4.35','userPassword')

syntax_registry.registerAttrType(
  PwdAttribute.oid,[
    '1.3.6.1.4.1.42.2.27.8.1.1', # pwdAttribute (see draft-behera-ldap-password-policy)
  ]
)


class PwdPolicySubentry(DynamicDNSelectList):
  oid = 'PwdPolicySubentry-oid'
  desc = 'DN of the pwdPolicy entry to be used for a certain entry'
  ldap_url = 'ldap:///_??sub?(|(objectClass=pwdPolicy)(objectClass=ds-cfg-password-policy))'

syntax_registry.registerAttrType(
  PwdPolicySubentry.oid,[
    '1.3.6.1.4.1.42.2.27.8.1.23', # pwdPolicySubentry
  ]
)


class PwdMaxAge(Timespan):
  oid = 'PwdMaxAge-oid'
  desc = 'pwdPolicy entry: Maximum age of user password'
  link_text = 'Search expired'
  title_text = u'Search for entries with this password policy and expired password'

  def _search_timestamp(self,diff_secs):
    return unicode(time.strftime('%Y%m%d%H%M%SZ',time.gmtime(time.time()-diff_secs)))

  def _timespan_search_params(self):
    return (
      ('search_attr','pwdChangedTime'),
      ('search_option',w2lapp.searchform.SEARCH_OPT_LE_THAN),
      ('search_string',self._search_timestamp(int(self.attrValue.strip()))),
    )

  def displayValue(self,valueindex=0,commandbutton=0):
    ts_dv = Timespan.displayValue(self,valueindex,commandbutton)
    # Possibly display a link
    ocs = self._entry.object_class_oid_set()
    if not commandbutton or not 'pwdPolicy' in ocs:
      return ts_dv
    try:
      ts_search_params = self._timespan_search_params()
    except ValueError,KeyError:
      return ts_dv
    search_link = self._form.applAnchor(
      'searchform',self.link_text,self._sid,
      (
        ('dn',self._dn),
        ('searchform_mode','adv'),
        ('search_attr','pwdPolicySubentry'),
        ('search_option',w2lapp.searchform.SEARCH_OPT_IS_EQUAL),
        ('search_string',self._dn),
      ) + ts_search_params,
      title=self.title_text,
    )
    return ' '.join((ts_dv,search_link))

syntax_registry.registerAttrType(
  PwdMaxAge.oid,[
    '1.3.6.1.4.1.42.2.27.8.1.3', # pwdMaxAge
  ]
)


class PwdExpireWarning(PwdMaxAge):
  oid = 'PwdExpireWarning-oid'
  desc = 'pwdPolicy entry: Password warning period'
  link_text = 'Search soon to expire'
  title_text = u'Search for entries with this password policy and soon to expire password'

  def _timespan_search_params(self):
    pwd_expire_warning = int(self.attrValue.strip())
    pwd_max_age = int(self._entry['pwdMaxAge'][0].strip())
    warn_timestamp = pwd_max_age-pwd_expire_warning
    return (
      ('search_attr','pwdChangedTime'),
      ('search_option',w2lapp.searchform.SEARCH_OPT_GE_THAN),
      ('search_string',self._search_timestamp(pwd_max_age)),
      ('search_attr','pwdChangedTime'),
      ('search_option',w2lapp.searchform.SEARCH_OPT_LE_THAN),
      ('search_string',self._search_timestamp(warn_timestamp)),
    )

syntax_registry.registerAttrType(
  PwdExpireWarning.oid,[
    '1.3.6.1.4.1.42.2.27.8.1.7', # pwdExpireWarning
  ]
)


class PwdChangedTime(GeneralizedTime):
  oid = 'PwdChangedTime-oid'
  desc = 'user entry: Last password change time'
  time_divisors = (
    (u'weeks',604800),
    (u'days',86400),
    (u'hours',3600),
    (u'mins',60),
    (u'secs',1),
  )

  def displayValue(self,valueindex=0,commandbutton=0):
    gt_disp_html = GeneralizedTime.displayValue(self,valueindex,commandbutton)
    try:
      pwd_changed_timestamp = timegm(utctime.strptime(self.attrValue))
    except ValueError:
      return gt_disp_html
    try:
      pwd_policy_subentry_dn = self._entry['pwdPolicySubentry'][0].decode(self._ls.charset)
    except KeyError:
      return gt_disp_html
    try:
      _,pwd_policy_entry = self._ls.readEntry(pwd_policy_subentry_dn,attrtype_list=['pwdMaxAge','pwdExpireWarning'])[0]
    except (LDAPError,TypeError,IndexError),e:
      return gt_disp_html
    try:
      pwd_max_age = int(pwd_policy_entry['pwdMaxAge'][0])
    except KeyError:
      expire_msg = 'will never expire'
    except ValueError:
      return gt_disp_html
    else:
      if pwd_max_age:
        current_time = time.time()
        expire_timestamp = pwd_changed_timestamp+pwd_max_age
        expired_since = current_time - expire_timestamp
        expire_msg = '%s %s / %s' % (
          {
            True:'expired since',
            False:'will expire',
          }[expired_since>0],
          utctime.strftimeiso8601(time.gmtime(expire_timestamp)),
          self._form.utf2display(w2lapp.gui.ts2repr(Timespan.time_divisors,u' ',abs(expired_since))),
        )
      else:
        expire_msg = 'will never expire'
    return '<br>'.join((gt_disp_html,expire_msg))

syntax_registry.registerAttrType(
  PwdChangedTime.oid,[
    '1.3.6.1.4.1.42.2.27.8.1.16', # pwdChangedTime
  ]
)


syntax_registry.registerAttrType(
  UserPassword.oid,[
    '1.3.6.1.4.1.42.2.27.8.1.20', # pwdHistory
  ]
)


syntax_registry.registerAttrType(
  Timespan.oid,[
    '1.3.6.1.4.1.42.2.27.8.1.2', # pwdMinAge
  ]
)


# Register all syntax classes in this module
for name in dir():
  syntax_registry.registerSyntaxClass(eval(name))

