Index: /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/apv/doc/en/admintools.xml
===================================================================
--- /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/apv/doc/en/admintools.xml	(revision 38449)
+++ /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/apv/doc/en/admintools.xml	(working copy)
@@ -5730,6 +5730,10 @@
                             <verbose_name>TAC_X</verbose_name>
                             <help_text>Uses the TACACAS server for external authentication.</help_text>
                         </choice>
+                        <choice value="ldaps">
+                            <verbose_name>LDAPS</verbose_name>
+                            <help_text>Uses the LDAPS server for external authentication.</help_text>
+                        </choice>
                     </choices>
                 </field>
                 <field name="admin_aaa_server" type="asso">
@@ -5750,11 +5754,15 @@
                     <help_text/>
                     <choices>
                         <choice value="es01">
-                            <verbose_name>es01</verbose_name>
+                            <verbose_name>es01 (RADIUS)</verbose_name>
                             <help_text/>
                         </choice>
                         <choice value="es02">
-                            <verbose_name>es02</verbose_name>
+                            <verbose_name>es02 (TACACS)</verbose_name>
+                            <help_text/>
+                        </choice>
+                        <choice value="es04">
+                            <verbose_name>es04 (LDAPS)</verbose_name>
                             <help_text/>
                         </choice>
                     </choices>
@@ -5771,7 +5779,16 @@
                     <verbose_name>Secret</verbose_name>
                     <help_text>Sets the secret shared with the external authentication server.</help_text>
                 </field>
+                <field name="distinguished_name" type="string">
+                    <verbose_name>Distinguished Name</verbose_name>
+                    <help_text>Example: OU=Development,DC=ARRAY,DC=NET</help_text>
+                </field>
+                <field name="member_of" type="string">
+                    <verbose_name>Member Of</verbose_name>
+                    <help_text>Example: CN=Engineering,OU=Development,DC=ARRAY,DC=NET</help_text>
+                </field>
             </fieldgrp>
         </model>
     </package>
-</package>
\ No newline at end of file
+</package>
+
Index: /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/apv/models/admintools/user/__init__.py
===================================================================
--- /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/apv/models/admintools/user/__init__.py	(revision 38449)
+++ /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/apv/models/admintools/user/__init__.py	(working copy)
@@ -5,9 +5,9 @@
 from hive.exceptions import ModelQueryException
 from hive.model.action import Action
 from hive.model.base import ANModel
-from hive.model.fields import FieldGroup, ADVANCED, ValueCondition
+from hive.model.fields import FieldGroup, ADVANCED, ValueCondition, BASIC
 from hive.model.fields.builtin import AssoField2, BooleanField, CharField, PasswordField, EnumField, UnionField, \
-    URLField, TextField, ListField, PortField
+    URLField, TextField, ListField, PortField, IntegerField, GroupField, IPv4AddressField, ImportLocalFileField
 from hive.model.legacycli import EasyParser, BlankParser, CLICmdError, RegexParser, CLICmdNormal, MATCHONE, MATCHALL, \
     CLICmdInfo, cli_parse, CLICmdWarning
 from hive.model.manager import CLIManager, QuerySet, UpdatingFields
@@ -43,18 +43,18 @@
     })
     ssh = FieldGroup(verbose_name=_('SSH Public Key'),level=ADVANCED, fields={
                     'ssh' : AssoField2(verbose_name=_('SSH Public Key'),tgt='admintools.user.SSHKey.user',
-                                       monitorable=True, mul='1', pos='left', optional=True, editable=True)				
+                                       monitorable=True, mul='1', pos='left', optional=True, editable=True)
                     })
     enable = FieldGroup(editable=False,verbose_name=_('SSH Key/Password Authentication'),level=ADVANCED,fields={
                         'sshkeys': BooleanField(verbose_name=_('SSH Key Authentication'),default=False,editable=True,optional=True),
                         'password': BooleanField(verbose_name=_('SSH Password Authentication'),default=True,editable=True),
-                        })	
-    
+                        })
+
     twofactor = FieldGroup(editable=False,verbose_name=_('WebUI Two Factor Verify'),level=ADVANCED,condition=ValueCondition('two_factor_condition',True),fields={
                         'two_factor': BooleanField(verbose_name=_('WebUI Two Factor Verify'),default=False,editable=True,optional=True),
                         'user_cert' : AssoField2(verbose_name=_('User Certificate'),tgt='admintools.user.UserCert.user',
                                        monitorable=True, mul='1', pos='left', optional=True, editable=True)
-                        })				
+                        })
 
     class Clear(Action):
         verbose_name = _('Clear')
@@ -179,7 +179,7 @@
                     RegexParser('Key length:', MATCHONE, match_exception=CLICmdNormal, exclusive=True),
                     BlankParser(nonblank_exception=CLICmdError, supplement=True))
             elif 'manual' in options['using']:
-                url = options['using']['manual']				
+                url = options['using']['manual']
                 result = self.cli.cmd('ssh import key "%s" \n%s\n...' % (options['__pk_list'][0]['username'],url),
                     RegexParser('Key Import Failed:', MATCHONE, match_exception=CLICmdError,match_msg=__('Key Import Failed: unknown key format')),
                     RegexParser('Enter the public key and use "..." on a new line', MATCHONE, match_exception=CLICmdNormal, exclusive=True),
@@ -540,7 +540,7 @@
                     'length': 'N/A',
                     'finger': output[1][0]['finger'],
                     'publickey': ''
-                }			
+                }
             rtn.append(result)
             return QuerySet(self._model, rtn)
 
@@ -562,26 +562,99 @@
     method = EnumField(verbose_name = _('Method'),default='radius',optional=True,values=(
                     ('radius','RADIUS'),
                     ('tac_x','TAC_X'),
+                    ('ldaps','LDAPS'),
                     ))
     admin_aaa_server = AssoField2(verbose_name='Admin AAA Server', tgt='admintools.user.AdminAAAServer.asso', mul='1', pos='left', optional=True)
 
+    certificate_settings = FieldGroup(verbose_name=_('Certificate Settings'), level=ADVANCED, fields={
+        'enable_certificate_verification': BooleanField(verbose_name='Enable Certificate Verification', default=False, lexical=('enable', 'disable'),
+                                    editable=True),
+        'root_ca': AssoField2(verbose_name=_('Root CA'), tgt='admintools.user.RootCACert.root_ca_cert', mul='1', pos='left', optional=True, editable=False),
+        'client_cert': AssoField2(verbose_name=_('Client Certificates'), tgt='admintools.user.ClientCert.asso', mul='1', pos='left',optional=True, editable=False)
+    })
+
     class Meta:
         profile = True
         verbose_name = _('Admin AAA Settings')
-    
+
+    class DeleteClientCert(Action):
+        verbose_name = _('Delete')
+        action_name = _('Delete')
+        btn_class = 'btn-danger slow-action'
+        confirm_msg = "Are you sure to perform the deletion?"
+        alert_msg = ""
+        process_title = ""
+        forever = False
+        related = 'client_cert'
+        config_change = True
+        instance_mul = '1..*'
+
+    class DeleteRootCACert(Action):
+        verbose_name = _('Delete')
+        action_name = _('Delete')
+        btn_class = 'btn-danger slow-action'
+        confirm_msg = "Are you sure to perform the deletion?"
+        alert_msg = ""
+        process_title = ""
+        forever = False
+        related = 'root_ca'
+        config_change = True
+        instance_mul = '1..*'
+
+    class ImportClientCert(Action):
+        verbose_name = _('Import')
+        action_name = _('Import')
+        btn_class = 'btn-danger slow-action'
+        confirm_msg = "You may overwrite an existing client certificate files (if any)."
+        alert_msg = ""
+        process_title = ""
+        related = 'client_cert'
+        forever = False
+        refresh_page = True
+        config_change = True
+        option_fields = (
+            UnionField(name='using',verbose_name=_('Using'), optional=True, fields={
+                'online': GroupField(verbose_name=_('FTP, TFTP or HTTP URL'),fields={
+                    'certificate_url': CharField(verbose_name=_('Client Certificate URL'), editable=True),
+                    'key_url': CharField(verbose_name=_('Client Certificate Key URL'), editable=True),
+                }),
+            }),
+        )
+
+    class ImportRootCACert(Action):
+        verbose_name = _('Import')
+        action_name = _('Import')
+        btn_class = 'btn-danger slow-action'
+        confirm_msg = "You may overwrite an existing Root CA certificate file (if any)."
+        alert_msg = ""
+        process_title = ""
+        related = 'root_ca'
+        forever = False
+        config_change = True
+        refresh_page = True
+        option_fields = (
+            UnionField(name='using',verbose_name=_('Using'), optional=True, fields={
+                      'online': GroupField(verbose_name=_('FTP, TFTP or HTTP URL'),fields={
+                            'file_url': CharField(verbose_name=_('ROOT CA Certificate URL'), editable=True),
+                      }),
+            }),
+        )
+
     class Manager(CLIManager):
         def _get_query_set(self):
             self.cli.set_enable()
             result = self.cli.cmd('show admin aaa all',
                                    RegexParser('admin aaa (?P<enable_aaa>on|off)', MATCHONE, reflags=re.S),
                                    RegexParser('admin aaa on (?P<priority>0|1)', MATCHONE, reflags=re.S),
-                                   RegexParser('admin aaa method (?P<method>RADIUS|TAC_X)', MATCHONE, reflags=re.S),
-                                   RegexParser('admin aaa authorize (?P<enable_aaa_authorize>on|off)', MATCHONE, reflags=re.S))
+                                   RegexParser('admin aaa method (?P<method>RADIUS|TAC_X|LDAPS)', MATCHONE, reflags=re.S),
+                                   RegexParser('admin aaa authorize (?P<enable_aaa_authorize>on|off)', MATCHONE, reflags=re.S),
+                                   RegexParser('admin aaa server es04 verifycert (?P<enable_certificate_verification>0|1)', MATCHONE, reflags=re.S))
             rtn_dict = {
                         'enable_aaa':True if result[0]['enable_aaa'] == 'on' else False,
                         'priority':result[1]['priority'] if result[1] else 0,
                         'method':result[2]['method'] if result[2] else '',
                         'enable_aaa_authorize':True if result[3]['enable_aaa_authorize'] == 'on' else False,
+                        'enable_certificate_verification': result[4]['enable_certificate_verification'] if result[4] else 0,
                         }
             self._model._meta.mark_delay_query(rtn_dict)
 
@@ -594,7 +667,14 @@
                                         BlankParser(nonblank_exception=CLICmdError, supplement=True))
             else:
                 result = self.cli.cmd('admin aaa authorize off',
-                                        BlankParser(nonblank_exception=CLICmdError, supplement=True))                
+                                        BlankParser(nonblank_exception=CLICmdError, supplement=True))
+            return result
+
+        def _update_enable_certificate_verification(self, instance):
+            self.cli.set_config()
+            instance.enable_certificate_verification = 1 if instance.enable_certificate_verification else 0
+            result = self.cli.cmd('admin aaa server es04 verifycert %u' % instance.enable_certificate_verification,
+                                  BlankParser(nonblank_exception=CLICmdError, supplement=True))
             return result
 
         @UpdatingFields(['enable_aaa', 'priority'])
@@ -612,7 +692,57 @@
             self.cli.set_config()
             result = self.cli.cmd('admin aaa method %s' % instance.method,
                                     BlankParser(nonblank_exception=CLICmdError, supplement=True))
-            return result          
+            return result
+
+        def _perform_ImportRootCACert(self, options):
+            self.cli.set_config()
+            result = None
+            if 'online' in options['using']:
+                result = self.cli.cmd('admin aaa server es04 rootca %s\nYES\n' % (options['using']['online']['file_url']),
+                                  RegexParser('wrong format', match_exception=CLICmdError, match_msg='Import Unsuccessful - wrong format'),
+                                  RegexParser('import failed', match_exception=CLICmdError, match_msg='Import Unsuccessful - Rootca certificate import failed. Try again'),
+                                  RegexParser('already exists', match_exception=CLICmdError, match_msg='Import Unsuccessful - already exists'),
+                                  RegexParser('invalid', match_exception=CLICmdError, match_msg='Import Unsuccessful - invalid root cert'),
+                                  RegexParser('import successful', match_exception=CLICmdNormal, exclusive=True),
+                                  BlankParser(nonblank_exception=CLICmdError, ignore_msg=['You may overwrite an existing client certificate file.\nType YES to continue, NO to abort: '], supplement=True))
+            return result
+
+
+        def _perform_ImportClientCert(self, options):
+            self.cli.set_config()
+            cert_result = None
+            cert_key_result = None
+            if 'online' in options['using']:
+                cert_result = self.cli.cmd('admin aaa server es04 clientcert %s\nYES\n' % (options['using']['online']['certificate_url']),
+                                  RegexParser('wrong format', match_exception=CLICmdError, match_msg='Import Unsuccessful - wrong format'),
+                                  RegexParser('already exists', match_exception=CLICmdError, match_msg='Import Unsuccessful - already exists'),
+                                  RegexParser('invalid', match_exception=CLICmdError, match_msg='Import Unsuccessful - invalid root cert'),
+                                  RegexParser('import successful', match_exception=CLICmdNormal, exclusive=True),
+                                  BlankParser(nonblank_exception=CLICmdError, supplement=True, ignore_msg=['You may overwrite an existing client certificate file.\nType YES to continue, NO to abort: ']))
+                cert_key_result = self.cli.cmd('admin aaa server es04 clientkey %s\nYES\n' % (options['using']['online']['key_url']),
+                                  RegexParser('wrong format', match_exception=CLICmdError, match_msg='Import Unsuccessful - wrong format'),
+                                  RegexParser('already exists', match_exception=CLICmdError, match_msg='Import Unsuccessful - already exists'),
+                                  RegexParser('invalid', match_exception=CLICmdError, match_msg='Import Unsuccessful - invalid root cert'),
+                                  RegexParser('invalid', match_exception=CLICmdError, match_msg='Import Unsuccessful - invalid root cert'),
+                                  RegexParser('import successful', match_exception=CLICmdNormal, exclusive=True),
+                                  BlankParser(nonblank_exception=CLICmdError, supplement=True, ignore_msg=['You may overwrite an existing client certificate file.\nType YES to continue, NO to abort: ']))
+            return cert_key_result
+
+
+        def _perform_DeleteRootCACert(self, options):
+            self.cli.set_config()
+            self.cli.cmd('no admin aaa server es04 rootca',
+                        BlankParser(nonblank_exception=CLICmdError, supplement=True))
+            return
+
+
+        def _perform_DeleteClientCert(self, options):
+            self.cli.set_config()
+            self.cli.cmd('no admin aaa server es04 clientcert',
+                        BlankParser(nonblank_exception=CLICmdError, supplement=True))
+            self.cli.cmd('no admin aaa server es04 clientkey',
+                        BlankParser(nonblank_exception=CLICmdError, supplement=True))
+            return
 
 
 class AdminAAAServer(ANModel):
@@ -620,10 +750,13 @@
     server_id = EnumField(verbose_name = _('Server ID'), default='es01', values=(
                     ('es01','es01'),
                     ('es02','es02'),
+                    ('es04', 'es04'),
                     ), primary_key=True)
     host_name = CharField(verbose_name=_('Host Name'))
-    aaa_port  = PortField(verbose_name=_('Port'))
-    secret = CharField(verbose_name=_('Secret'), optional=True)
+    aaa_port = PortField(verbose_name=_('Port'))
+    secret = CharField(verbose_name=_('Secret'), default='', condition=ValueCondition('server_id', ['es01', 'es02']))
+    distinguished_name = CharField(verbose_name=_('Domain'), default='', condition=ValueCondition('server_id', ['es04']))
+    member_of = CharField(verbose_name=_('MemberOf'), default='', condition=ValueCondition('server_id', ['es04']))
 
     class Meta:
         verbose_name = _('Admin AAA Server')
@@ -632,11 +765,23 @@
     class Manager(CLIManager):
         def _get_query_set(self):
             self.cli.set_enable()
-            result = self.cli.cmd('show admin aaa all',
-                                   RegexParser('admin aaa server (?P<server_id>es01|es02) "(?P<host_name>.*?)" (?P<aaa_port>[0-9]+) "(?P<secret>.*?)"', MATCHALL, reflags=re.S))
+            regex_parser = [
+                RegexParser('admin aaa server (?P<server_id>es01|es02) "(?P<host_name>.*?)" (?P<aaa_port>[0-9]+) "(?P<secret>.*?)"', MATCHALL, reflags=re.S),
+                RegexParser('admin aaa server (?P<server_id>es04) settings "(?P<host_name>.*?)" (?P<aaa_port>[0-9]+) "(?P<distinguished_name>.*?)" "(?P<member_of>.*?)"',MATCHALL, reflags=re.S)
+            ]
+            result = self.cli.cmd('show admin aaa all', regex_parser)
+            admin_aaa_server_result = []
             for data in result:
-              self._model._meta.mark_delay_query(data)
-            return QuerySet(self._model, result)
+                for aaa_server_data in data:
+                    if 'secret' not in aaa_server_data:
+                        aaa_server_data['secret'] = None
+                    if 'distinguished_name' not in aaa_server_data:
+                        aaa_server_data['distinguished_name'] = None
+                    if 'member_of' not in aaa_server_data:
+                        aaa_server_data['member_of'] = None
+                    admin_aaa_server_result.append(aaa_server_data)
+                    self._model._meta.mark_delay_query(aaa_server_data)
+            return QuerySet(self._model, admin_aaa_server_result)
 
         def _filter(self, filter_dict):
             if len(filter_dict) == 1:
@@ -647,13 +792,86 @@
         def _insert(self, instance):
             data = instance.get_field_dict()
             self.cli.set_config()
-            result = self.cli.cmd('admin aaa server %(server_id)s "%(host_name)s" %(aaa_port)u "%(secret)s"' % data,
-                                    BlankParser(nonblank_exception=CLICmdError, supplement=True))  
+            cli_cmd = ''
+            if 'server_id' in data and (data['server_id'] == 'es01' or data['server_id'] == 'es02'):
+                cli_cmd = 'admin aaa server %(server_id)s "%(host_name)s" %(aaa_port)u "%(secret)s"' % data
+            if 'server_id' in data and data['server_id'] == 'es04':
+                cli_cmd = 'admin aaa server %(server_id)s settings "%(host_name)s" %(aaa_port)u "%(distinguished_name)s" "%(member_of)s"' % data
+            result = self.cli.cmd(cli_cmd, BlankParser(nonblank_exception=CLICmdError, supplement=True))
             return result
-        
+
         def _delete(self, pk_list):
             self.cli.set_config()
             for each in pk_list:
-                self.cli.cmd('no admin aaa server "%s"' % each['server_id'],
-                             BlankParser(nonblank_exception=CLICmdError, supplement=True))
+                cli_cmd = 'no admin aaa server %s' % each['server_id']
+                if each["server_id"] == "es04":
+                    cli_cmd = 'no admin aaa server %s settings' % each['server_id']
+                self.cli.cmd(cli_cmd, BlankParser(nonblank_exception=CLICmdError, supplement=True))
             return
+
+
+class RootCACert(ANModel):
+    root_ca_cert = AssoField2(tgt='admintools.user.AdminAAASettings.root_ca', hidden=True,
+                      mul='*', pos='right', default=[{}])
+    index = IntegerField(verbose_name=_('Index'), optional=True, editable=False, hidden=True, primary_key=True)
+    certificate_text = CharField(verbose_name=_('Certificate Text'), optional=True, editable=False)
+
+    class Meta:
+        verbose_name = _('Root CA Certificate')
+        list_config_options = {'add_button_hide': True, 'click_enable':False, 'delete_button_hide': True, 'columns': [
+            {'name': 'index'},
+            {'name': 'certificate_text'},
+        ]}
+
+    class Manager(CLIManager):
+        def _get_query_set(self):
+            self.cli.set_enable()
+            datas = []
+            output = self.cli.cmd('show admin aaa all')
+            certs_array = output.split('----- Root CA -----')
+            if len(certs_array) > 1:
+                certs_array = certs_array[1]
+                root_ca_array = certs_array.split('-----END CERTIFICATE-----')[:-1]
+                index = 0
+                for each_cert in root_ca_array:
+                    index += 1
+                    each_cert += '-----END CERTIFICATE-----'
+                    data = {'certificate_text': each_cert, 'index': index}
+                    datas.append(data)
+            return QuerySet(self._model, datas)
+
+
+class ClientCert(ANModel):
+    asso = AssoField2(tgt='admintools.user.AdminAAASettings.client_cert', hidden=True,
+                      mul='*', pos='right', default=[{}])
+    index = IntegerField(verbose_name=_('Index'), optional=True, editable=False, hidden=True, primary_key=True)
+    certificate_text = CharField(verbose_name=_('Certificate Text'), optional=True, editable=False)
+
+    class Meta:
+        verbose_name = _('Client Certificate')
+        list_config_options = {'add_button_hide': True, 'click_enable':False, 'delete_button_hide': True, 'columns': [
+            {'name': 'index'},
+            {'name': 'certificate_text'},
+        ]}
+
+    class Manager(CLIManager):
+        def _get_query_set(self):
+            self.cli.set_enable()
+            datas = []
+            output = self.cli.cmd('show admin aaa all')
+            certs_array = output.split('----- Client Certificate -----')
+            if len(certs_array) > 1:
+                certs_array = certs_array[1]
+                certs_array = certs_array.split('----- Root CA -----')
+                if len(certs_array) > 0:
+                    certs_array = certs_array[0]
+                    client_cert_array = certs_array.split('-----END CERTIFICATE-----')[:-1]
+                    index = 0
+                    for each_cert in client_cert_array:
+                        index += 1
+                        each_cert += '-----END CERTIFICATE-----'
+                        data = {'certificate_text': each_cert, 'index': index}
+                        datas.append(data)
+            return QuerySet(self._model, datas)
+
+
