URL: https://github.com/freeipa/freeipa/pull/559
Author: pvomacka
 Title: #559: WebUI: Certificate login
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/559/head:pr559
git checkout pr559
From b3b6fd6513df570639827c260c895a369322fca4 Mon Sep 17 00:00:00 2001
From: Pavel Vomacka <pvoma...@redhat.com>
Date: Thu, 9 Mar 2017 12:14:21 +0100
Subject: [PATCH 1/2] Support certificate login after installation and upgrade

Add necessary steps which set SSSD and set SELinux boolean during
installation or upgrade. Also create new endpoint in apache for
login using certificates.

https://pagure.io/freeipa/issue/6225
---
 freeipa.spec.in                     |  1 +
 install/conf/ipa.conf               | 25 ++++++++++++++++++++++++-
 ipaclient/install/client.py         | 20 ++++++++++++++++++++
 ipaserver/install/httpinstance.py   |  1 +
 ipaserver/install/server/upgrade.py |  5 +++++
 5 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index db591e0..af76a7d 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -255,6 +255,7 @@ Requires: mod_wsgi
 Requires: mod_auth_gssapi >= 1.5.0
 Requires: mod_nss >= 1.0.8-26
 Requires: mod_session
+Requires: mod_lookup_identity
 Requires: python-ldap >= 2.4.15
 Requires: python-gssapi >= 1.2.0
 Requires: acl
diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf
index 419d4e3..44d07a1 100644
--- a/install/conf/ipa.conf
+++ b/install/conf/ipa.conf
@@ -4,8 +4,13 @@
 # This file may be overwritten on upgrades.
 #
 
-ProxyRequests Off
+# Load lookup_identity module in case it has not been loaded yet
+# The module is used to search users according the certificate.
+<IfModule !lookup_identity_module>
+    LoadModule lookup_identity_module modules/mod_lookup_identity.so
+</IfModule>
 
+ProxyRequests Off
 
 #We use xhtml, a file format that the browser validates
 DirectoryIndex index.html
@@ -97,6 +102,24 @@ Alias /ipa/session/cookie "/usr/share/ipa/gssapi.login"
   Allow from all
 </Location>
 
+# Login with user certificate/smartcard configuration
+# This configuration needs to be loaded after <Location "/ipa">
+<Location "/ipa/session/login_x509">
+  AuthType none
+  Require all granted
+  GssapiCredStore keytab:/var/lib/ipa/gssproxy/http.keytab
+  GssapiCredStore client_keytab:/var/lib/ipa/gssproxy/http.keytab
+  GssapiDelegCcacheDir /var/run/ipa/ccaches
+  GssapiDelegCcachePerms mode:0660 gid:ipaapi
+  GssapiImpersonate On
+  NSSVerifyClient require
+  NSSOCSP on
+  NSSUserName SSL_CLIENT_CERT
+  LookupUserByCertificate On
+  WSGIProcessGroup ipa
+  WSGIApplicationGroup ipa
+</Location>
+
 <Location "/ipa/session/change_password">
   Satisfy Any
   Order Deny,Allow
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
index 774eaaf..579d1aa 100644
--- a/ipaclient/install/client.py
+++ b/ipaclient/install/client.py
@@ -846,6 +846,9 @@ def configure_sssd_conf(
         sssdconfig.new_config()
         domain = sssdconfig.new_domain(cli_domain)
 
+    if options.on_master:
+        sssd_enable_service(sssdconfig, 'ifp')
+
     if (
         (options.conf_ssh and file_exists(paths.SSH_CONFIG)) or
         (options.conf_sshd and file_exists(paths.SSHD_CONFIG))
@@ -948,6 +951,23 @@ def configure_sssd_conf(
     return 0
 
 
+def sssd_enable_service(sssdconfig, service):
+    try:
+        sssdconfig.new_service(service)
+    except SSSDConfig.ServiceAlreadyExists:
+        pass
+    except SSSDConfig.ServiceNotRecognizedError:
+        root_logger.error(
+            "Unable to activate the %s service in SSSD config.", service)
+        root_logger.info(
+            "Please make sure you have SSSD built with %s support "
+            "installed.", service)
+        root_logger.info(
+            "Configure %s support manually in /etc/sssd/sssd.conf.", service)
+
+    sssdconfig.activate_service(service)
+
+
 def change_ssh_config(filename, changes, sections):
     if not changes:
         return True
diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
index 0c2216e..b1f5986 100644
--- a/ipaserver/install/httpinstance.py
+++ b/ipaserver/install/httpinstance.py
@@ -53,6 +53,7 @@
     httpd_can_network_connect='on',
     httpd_manage_ipa='on',
     httpd_run_ipa='on',
+    httpd_dbus_sssd='on',
 )
 
 HTTPD_USER = constants.HTTPD_USER
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index b19c2f0..993835e 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -23,6 +23,7 @@
 import SSSDConfig
 import ipalib.util
 import ipalib.errors
+from ipaclient.install.client import sssd_enable_service
 from ipaplatform import services
 from ipaplatform.tasks import tasks
 from ipapython import ipautil, version, certdb
@@ -1771,6 +1772,10 @@ def upgrade_configuration():
 
     set_sssd_domain_option('ipa_server_mode', 'True')
 
+    sssdconfig = SSSDConfig.SSSDConfig()
+    sssdconfig.import_config()
+    sssd_enable_service(sssdconfig, 'ifp')
+
     krb = krbinstance.KrbInstance(fstore)
     krb.fqdn = fqdn
     krb.realm = api.env.realm

From 03d32ee2b342c162396f038f3ab10b0386302ee5 Mon Sep 17 00:00:00 2001
From: Pavel Vomacka <pvoma...@redhat.com>
Date: Thu, 9 Mar 2017 12:17:00 +0100
Subject: [PATCH 2/2] WebUI: add link to login page which for login using
 certificate

Also add error message when login failed.

https://pagure.io/freeipa/issue/6225
---
 install/ui/src/freeipa/auth.js                    |  4 +-
 install/ui/src/freeipa/widgets/LoginScreen.js     | 73 ++++++++++++++++++++++-
 install/ui/src/freeipa/widgets/LoginScreenBase.js |  5 ++
 3 files changed, 78 insertions(+), 4 deletions(-)

diff --git a/install/ui/src/freeipa/auth.js b/install/ui/src/freeipa/auth.js
index 5e160a7..992b54a 100644
--- a/install/ui/src/freeipa/auth.js
+++ b/install/ui/src/freeipa/auth.js
@@ -111,7 +111,7 @@ auth.Auth = declare([Stateful, Evented], {
      * Enabled auth methods
      * @property {string[]}
      */
-    auth_methods: ['kerberos', 'password'],
+    auth_methods: ['kerberos', 'password', 'certificate'],
 
     /**
      * Authenticated user's Kerberos principal
@@ -249,4 +249,4 @@ auth.Auth = declare([Stateful, Evented], {
 
 auth.current = new auth.Auth();
 return auth;
-});
\ No newline at end of file
+});
diff --git a/install/ui/src/freeipa/widgets/LoginScreen.js b/install/ui/src/freeipa/widgets/LoginScreen.js
index 0096433..b99b517 100644
--- a/install/ui/src/freeipa/widgets/LoginScreen.js
+++ b/install/ui/src/freeipa/widgets/LoginScreen.js
@@ -19,10 +19,12 @@
 */
 
 define(['dojo/_base/declare',
+        'dojo/Deferred',
         'dojo/dom-construct',
         'dojo/dom-style',
         'dojo/query',
         'dojo/on',
+        'dojo/topic',
         '../ipa',
         '../auth',
         '../reg',
@@ -31,7 +33,7 @@ define(['dojo/_base/declare',
         '../util',
         './LoginScreenBase'
        ],
-       function(declare, construct, dom_style, query, on,
+       function(declare, Deferred, construct, dom_style, query, on, topic,
                 IPA, auth, reg, FieldBinder, text, util, LoginScreenBase) {
 
 
@@ -55,11 +57,15 @@ define(['dojo/_base/declare',
                     " have valid tickets (obtainable via kinit) and " +
                     "<a href='http://${host}/ipa/config/unauthorized.html'>configured</a>" +
                     " the browser correctly, then click Login. ",
+        cert_msg: "<i class=\"fa fa-info-circle\"></i> To login with <strong>Smart Card</strong>," +
+              " please make sure you have valid personal certificate. ",
 
         form_auth_failed: "Login failed due to an unknown reason. ",
 
         krb_auth_failed: "Authentication with Kerberos failed",
 
+        cert_auth_failed: "Authentication with personal certificate failed",
+
         password_expired: "Your password has expired. Please enter a new password.",
 
         password_change_complete: "Password change complete",
@@ -72,9 +78,12 @@ define(['dojo/_base/declare',
 
         user_locked: "The user account you entered is locked. ",
 
+        login_url: '/ipa/session/login_x509',
+
         //nodes:
         login_btn_node: null,
         reset_btn_node: null,
+        cert_btn_node: null,
 
         /**
          * View this form is in.
@@ -86,6 +95,16 @@ define(['dojo/_base/declare',
 
         render_buttons: function(container) {
 
+            this.cert_btn_node = IPA.button({
+                name: 'cert_auth',
+                title:"Login using personal certificate",
+                label: "Smart Card Login",
+                button_class: 'btn btn-link',
+                click: this.login_with_cert.bind(this)
+            })[0];
+            construct.place(this.cert_btn_node, container);
+            construct.place(document.createTextNode(" "), container);
+
             this.sync_btn_node = IPA.button({
                 name: 'sync',
                 label: text.get('@i18n:login.sync_otp_token', "Sync OTP Token"),
@@ -251,6 +270,18 @@ define(['dojo/_base/declare',
             }.bind(this));
         },
 
+        login_with_cert: function() {
+
+            this.lookup_credentials().then(function(status) {
+                if (status === 200) {
+                    this.emit('logged_in');
+                } else {
+                    var val_summary = this.get_widget('validation');
+                    val_summary.add_error('login', this.cert_auth_failed);
+                }
+            }.bind(this));
+        },
+
         login_and_reset: function() {
 
             var val_summary = this.get_widget('validation');
@@ -293,6 +324,40 @@ define(['dojo/_base/declare',
 
         },
 
+        lookup_credentials: function() {
+
+            var status;
+            var d = new Deferred();
+
+            function error_handler(xhr, text_status, error_thrown) {
+                d.resolve(xhr.status);
+                topic.publish('rpc-end');
+            }
+
+            function success_handler(data, text_status, xhr) {
+                auth.current.set_authenticated(true, 'kerberos');
+                d.resolve(xhr.status);
+                topic.publish('rpc-end');
+            }
+
+            var login = this.get_field('username').get_value()[0];
+
+            var request = {
+                url: this.login_url,
+                cache: false,
+                type: "GET",
+                data: $.param({
+                    'username': login
+                }),
+                success: success_handler,
+                error: error_handler
+            };
+            topic.publish('rpc-start');
+            $.ajax(request);
+
+            return d.promise;
+        },
+
         refresh: function() {
             if (this.view === 'reset') {
                 this.show_reset_view();
@@ -307,7 +372,7 @@ define(['dojo/_base/declare',
                 var val_summary = this.get_widget('validation');
                 val_summary.add_info('expired', this.expired_msg);
             }
-            this.set_visible_buttons(['sync', 'login']);
+            this.set_visible_buttons(['cert_auth', 'sync', 'login']);
             if (this.password_enabled()) {
                 this.use_fields(['username', 'password']);
                 var username_f = this.get_field('username');
@@ -344,6 +409,10 @@ define(['dojo/_base/declare',
             if (this.kerberos_enabled()) {
                 aside += "<p>"+this.kerberos_msg+"<p/>";
             }
+            if (this.certificate_enabled()) {
+                aside += "<p>"+this.cert_msg+"<p/>";
+            }
+
             this.set('aside', aside);
         },
 
diff --git a/install/ui/src/freeipa/widgets/LoginScreenBase.js b/install/ui/src/freeipa/widgets/LoginScreenBase.js
index a1c986e..a8c207f 100644
--- a/install/ui/src/freeipa/widgets/LoginScreenBase.js
+++ b/install/ui/src/freeipa/widgets/LoginScreenBase.js
@@ -328,6 +328,11 @@ define(['dojo/_base/declare',
             return auth.current.auth_methods.indexOf('password') > -1;
         },
 
+        certificate_enabled: function() {
+            return auth.current.auth_methods.indexOf('certificate') > -1;
+        },
+
+
         postscript: function(args) {
             this.create_fields();
         },
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to