浏览代码

Merge pull request #2035 from RussWhitehead/ldap

gh-2013 - Enable LDAP pw checking in esp/ws_account/eclwatch

Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 13 年之前
父节点
当前提交
e5f508c003

+ 1 - 1
esp/eclwatch/ws_XSLT/CMakeLists.txt

@@ -171,7 +171,7 @@ FOREACH ( iFILES
     ${CMAKE_CURRENT_SOURCE_DIR}/access_users.xslt
     ${CMAKE_CURRENT_SOURCE_DIR}/account.xslt
     ${CMAKE_CURRENT_SOURCE_DIR}/account_input.xslt
-    ${CMAKE_CURRENT_SOURCE_DIR}/account_whoami.xslt
+    ${CMAKE_CURRENT_SOURCE_DIR}/account_myaccount.xslt
 )
     Install ( FILES ${iFILES} DESTINATION ${OSSDIR}/componentfiles/smc_xslt COMPONENT Runtime )
 ENDFOREACH ( iFILES )

+ 100 - 0
esp/eclwatch/ws_XSLT/account_myaccount.xslt

@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (C) <2010>  <LexisNexis Risk Data Management Inc.>
+
+    All rights reserved. This program is NOT PRESENTLY free software: you can NOT redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
+<xsl:output method="html"/>
+    <xsl:output method="html"/>
+    <xsl:template match="/">
+        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+        <head>
+            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+            <title>My Account</title>
+      <link rel="stylesheet" type="text/css" href="/esp/files/yui/build/fonts/fonts-min.css" />
+      <link rel="stylesheet" type="text/css" href="/esp/files/css/espdefault.css" />
+      <script type="text/javascript" src="/esp/files/scripts/espdefault.js">&#160;</script>
+      <script type="text/javascript" language="javascript">
+            <![CDATA[
+            function handleSubmitBtn()
+            {
+                document.getElementById("submit").disabled = (document.getElementById("oldpass").value=='') || (document.getElementById("newpass1").value=='') || (document.getElementById("newpass2").value=='');
+            }
+            ]]></script>
+        </head>
+          <body class="yui-skin-sam" onload="nof5();">
+            <p align="left" />
+            <xsl:apply-templates/>
+          </body>
+        </html>
+      </xsl:template>
+      <xsl:template match="MyAccountResponse">
+        <form name="esp_form" method="POST" action="/ws_account/MyAccount">
+          <table>
+            <tr>
+              <th colspan="5">
+                <h3>Your ESP Account</h3>
+              </th>
+            </tr>
+            <tr>
+              <td>
+                <b>
+                  <xsl:text>User Name: </xsl:text>
+                </b>
+              </td>
+              <td>
+                <xsl:value-of select="username"/>
+              </td>
+            </tr>
+            <tr>
+              <td>
+                <b>First Name: </b>
+              </td>
+              <td>
+                <xsl:value-of select="firstName"/>
+              </td>
+            </tr>
+            <tr>
+              <td>
+                <b>Last Name: </b>
+              </td>
+              <td>
+                <xsl:value-of select="lastName"/>
+              </td>
+            </tr>
+            <tr>
+              <td>
+                <b>Password Expiration: </b>
+              </td>
+              <td>
+                <xsl:value-of select="passwordExpiration"/>
+              </td>
+            </tr>
+            <xsl:if test="passwordDaysRemaining != -2">
+              <tr>
+                <td>
+                  <b>Days Until Expiration: </b>
+                </td>
+                <td>
+                  <xsl:value-of select="passwordDaysRemaining"/>
+                </td>
+              </tr>
+            </xsl:if>
+          </table>
+        </form>
+      </xsl:template>
+</xsl:stylesheet>

+ 0 - 42
esp/eclwatch/ws_XSLT/account_whoami.xslt

@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-    Copyright (C) <2010>  <LexisNexis Risk Data Management Inc.>
-
-    All rights reserved. This program is NOT PRESENTLY free software: you can NOT redistribute it and/or modify
-    it under the terms of the GNU Affero General Public License as
-    published by the Free Software Foundation, either version 3 of the
-    License, or (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU Affero General Public License for more details.
-
-    You should have received a copy of the GNU Affero General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
--->
-
-<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
-<xsl:output method="html"/>
-    <xsl:output method="html"/>
-    <xsl:template match="/">
-        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-        <head>
-            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-            <title>Who Am I</title>
-      <link rel="stylesheet" type="text/css" href="/esp/files/yui/build/fonts/fonts-min.css" />
-      <link rel="stylesheet" type="text/css" href="/esp/files/css/espdefault.css" />
-      <link rel="stylesheet" type="text/css" href="/esp/files/css/eclwatch.css" />
-      <script type="text/javascript" src="/esp/files/scripts/espdefault.js">&#160;</script>
-    </head>
-      <body class="yui-skin-sam" onload="nof5();">
-        <p align="left" />
-        <xsl:apply-templates/>
-      </body>
-        </html>
-    </xsl:template>
-    <xsl:template match="WhoAmIResponse">
-        <br/><b><xsl:text>You're currently logged in as </xsl:text><xsl:value-of select="username"/></b>
-    </xsl:template>
-</xsl:stylesheet>

+ 9 - 3
esp/scm/ws_account.ecm

@@ -27,15 +27,21 @@ ESPresponse [exceptions_inline] UpdateUserInputResponse
     string username;
 };
 
-ESPrequest WhoAmIRequest
+
+ESPrequest MyAccountRequest
 {
 };
 
-ESPresponse [exceptions_inline] WhoAmIResponse
+ESPresponse [exceptions_inline] MyAccountResponse
 {
     string username;
+    string firstName;
+    string lastName;
+    string passwordExpiration;
+    int    passwordDaysRemaining;
 };
 
+
 ESPrequest UpdateUserRequest
 {
     [label("User Name"), cols(20)] string username;
@@ -63,8 +69,8 @@ ESPresponse [exceptions_inline] VerifyUserResponse
 
 ESPservice [exceptions_inline("./smc_xslt/exceptions.xslt")] ws_account
 {
+    ESPmethod [client_xslt("/esp/xslt/account_myaccount.xslt")] MyAccount(MyAccountRequest, MyAccountResponse);
     ESPmethod [client_xslt("/esp/xslt/account_input.xslt")] UpdateUserInput(UpdateUserInputRequest, UpdateUserInputResponse);
-    ESPmethod [client_xslt("/esp/xslt/account_whoami.xslt")] WhoAmI(WhoAmIRequest, WhoAmIResponse);
     ESPmethod [resp_xsl_default("./smc_xslt/account.xslt")] UpdateUser(UpdateUserRequest, UpdateUserResponse);
 	ESPmethod VerifyUser(VerifyUserRequest, VerifyUserResponse);
 };

+ 20 - 1
esp/services/ws_account/ws_accountService.cpp

@@ -147,13 +147,30 @@ bool Cws_accountEx::onUpdateUserInput(IEspContext &context, IEspUpdateUserInputR
     return true;
 }
 
-bool Cws_accountEx::onWhoAmI(IEspContext &context, IEspWhoAmIRequest &req, IEspWhoAmIResponse &resp)
+bool Cws_accountEx::onMyAccount(IEspContext &context, IEspMyAccountRequest &req, IEspMyAccountResponse &resp)
 {
     try
     {
         ISecUser* user = context.queryUser();
         if(user != NULL)
         {
+            CDateTime dt;
+            user->getPasswordExpiration(dt);
+            StringBuffer sb;
+            if (dt.isNull())
+            {
+                assertex(user->getPasswordDaysRemaining() == -2);//-1 if expired, -2 if never expires
+                sb.append("Never");
+            }
+            else
+            {
+                dt.getString(sb);
+                sb.replace('T', (char)0);
+            }
+            resp.setPasswordExpiration(sb.str());
+            resp.setPasswordDaysRemaining(user->getPasswordDaysRemaining());
+            resp.setFirstName(user->getFirstName());
+            resp.setLastName(user->getLastName());
             resp.setUsername(user->getName());
         }
     }
@@ -163,6 +180,8 @@ bool Cws_accountEx::onWhoAmI(IEspContext &context, IEspWhoAmIRequest &req, IEspW
     }
     return true;
 }
+
+
 #endif
 
 bool Cws_accountEx::onVerifyUser(IEspContext &context, IEspVerifyUserRequest &req, IEspVerifyUserResponse &resp)

+ 4 - 4
esp/services/ws_account/ws_accountService.hpp

@@ -49,21 +49,21 @@ public:
         const char* build_level = getBuildLevel();
         if (!stricmp(m_authType.str(), "none") || !stricmp(m_authType.str(), "local"))
         {
+            ensureNavLink(*folder, "My Account", "/Ws_Access/SecurityNotEnabled?form_", "My Account", NULL, NULL, 0, true);//Force the menu to use this setting
             ensureNavLink(*folder, "Change Password", "/Ws_Access/SecurityNotEnabled?form_", "Change Password", NULL, NULL, 0, true);//Force the menu to use this setting
             if (!isFF)
                 ensureNavLink(*folder, "Relogin", "/Ws_Access/SecurityNotEnabled?form_", "Relogin", NULL, NULL, 0, true);//Force the menu to use this setting
             else
                 ensureNavLink(*folder, "Relogin", "/Ws_Access/FirefoxNotSupport?form_", "Relogin", NULL, NULL, 0, true);//Force the menu to use this setting
-            ensureNavLink(*folder, "Who Am I", "/Ws_Access/SecurityNotEnabled?form_", "WhoAmI", NULL, NULL, 0, true);//Force the menu to use this setting
         }
         else
         {
+            ensureNavLink(*folder, "My Account", "/Ws_Account/MyAccount", "MyAccount", NULL, NULL, 0, true);//Force the menu to use this setting
             ensureNavLink(*folder, "Change Password", "/Ws_Account/UpdateUserInput", "Change Password", NULL, NULL, 0, true);//Force the menu to use this setting
             if (!isFF)
                 ensureNavLink(*folder, "Relogin", "/Ws_Account/LogoutUser", "Relogin", NULL, NULL, 0, true);//Force the menu to use this setting
             else
                 ensureNavLink(*folder, "Relogin", "/Ws_Access/FirefoxNotSupport?form_", "Relogin", NULL, NULL, 0, true);//Force the menu to use this setting
-            ensureNavLink(*folder, "Who Am I", "/Ws_Account/WhoAmI", "WhoAmI", NULL, NULL, 0, true);//Force the menu to use this setting
         }
 #endif
     }
@@ -154,11 +154,11 @@ public:
 #ifdef _USE_OPENLDAP
     virtual bool onUpdateUser(IEspContext &context, IEspUpdateUserRequest &req, IEspUpdateUserResponse &resp);
     virtual bool onUpdateUserInput(IEspContext &context, IEspUpdateUserInputRequest &req, IEspUpdateUserInputResponse &resp);
-    virtual bool onWhoAmI(IEspContext &context, IEspWhoAmIRequest &req, IEspWhoAmIResponse &resp);
+    virtual bool onMyAccount(IEspContext &context, IEspMyAccountRequest &req, IEspMyAccountResponse &resp);
 #else
     virtual bool onUpdateUser(IEspContext &context, IEspUpdateUserRequest &req, IEspUpdateUserResponse &resp) {return true;};
     virtual bool onUpdateUserInput(IEspContext &context, IEspUpdateUserInputRequest &req, IEspUpdateUserInputResponse &resp) {return true;};
-    virtual bool onWhoAmI(IEspContext &context, IEspWhoAmIRequest &req, IEspWhoAmIResponse &resp) {return true;};
+    virtual bool onMyAccount(IEspContext &context, IEspMyAccountRequest &req, IEspMyAccountResponse &resp) {return true;};
 #endif
     virtual bool onVerifyUser(IEspContext &context, IEspVerifyUserRequest &req, IEspVerifyUserResponse &resp);
 };

+ 9 - 0
system/jlib/jtime.cpp

@@ -318,6 +318,15 @@ void CDateTime::setTime(unsigned hour, unsigned minute, unsigned second, unsigne
     set(year, month, day, hour, minute, second, nano, local);
 }
 
+//FILETIME is a large integer that represents the number of 100 nanosecond
+//intervals since January 1, 1601 (UTC), also known as a FILETIME value.
+void CDateTime::setFromFILETIME(__int64 fileTime)
+{
+    __int64 secsAfterADEpoch = fileTime / 10000000;
+    __int64 AD2Unix = ((1970-1601) * 365 - 3 + ((1970-1601)/4) ) * (__int64)86400;
+    set(secsAfterADEpoch - AD2Unix);
+}
+
 void CDateTime::setNow()
 {
     time_t simple;

+ 2 - 1
system/jlib/jtime.hpp

@@ -81,7 +81,8 @@ public:
     void setDate(unsigned year, unsigned month, unsigned day); // Sets to midnight UTC on date given
     void setTime(unsigned hour, unsigned minute, unsigned second, unsigned nano = 0, bool local = false); // Leaves the date along, set to the time given
     void set(time_t simple);
-    
+    void setFromFILETIME(__int64 fileTime);
+
     void setString(char const * str, char const * * end = NULL, bool local = false); // Sets to date and time given as yyyy-mm-ddThh:mm:ss[.nnnnnnnnn]
     void setDateString(char const * str, char const * * end = NULL); // Sets to midnight UTC on date given as yyyy-mm-dd
     void setTimeString(char const * str, char const * * end = NULL, bool local = false); // Leaves the date alone, sets to the time given as hh:mm:ss[.nnnnnnnnn]

+ 108 - 6
system/security/LdapSecurity/ldapconnection.cpp

@@ -887,6 +887,7 @@ private:
     //int                  m_defaultWorkunitScopePermission;
     Owned<CLdapConfig>   m_ldapconfig;
     StringBuffer         m_pwscheme;
+    bool                 m_passwordNeverExpires;
     class CLDAPMessage
     {
     public:
@@ -957,17 +958,58 @@ public:
         createLdapBasedn(NULL, m_ldapconfig->getResourceBasedn(rtype), PT_DEFAULT);
     }
 
+    virtual __int64 getMaxPwdAge()
+    {
+        char* attrs[] = {"maxPwdAge", NULL};
+        CLDAPMessage searchResult;
+        TIMEVAL timeOut = {LDAPTIMEOUT,0};
+        Owned<ILdapConnection> lconn = m_connections->getConnection();
+        LDAP* sys_ld = ((CLdapConnection*)lconn.get())->getLd();
+
+        int result = ldap_search_ext_s(sys_ld, (char*)m_ldapconfig->getBasedn(), LDAP_SCOPE_BASE, NULL,
+                                        attrs, 0, NULL, NULL, &timeOut, LDAP_NO_LIMIT, &searchResult.msg);
+        if(result != LDAP_SUCCESS)
+        {
+            DBGLOG("ldap_search_ext_s error: %s, when searching maxPwdAge", ldap_err2string( result ));
+            return 0;
+        }
+        unsigned entries = ldap_count_entries(sys_ld, searchResult);
+        if(entries == 0)
+        {
+            DBGLOG("ldap_search_ext_s error: Could not find maxPwdAge");
+            return 0;
+        }
+        char **values;
+        values = ldap_get_values(sys_ld, searchResult.msg, "maxPwdAge");
+        assertex(values);
+        char *val = values[0];
+        if (*val == '-')
+            ++val;
+        __int64 maxAge = 0;
+        for (int x=0; val[x]; x++)
+            maxAge = maxAge * 10 + ( (int)val[x] - '0');
+        ldap_value_free(values);
+        return maxAge;
+    }
+
     virtual bool authenticate(ISecUser& user)
     {
         {
             char        *attribute, **values;       
             BerElement  *ber;
+            struct berval** bvalues = NULL;
 
             const char* username = user.getName();
             const char* password = user.credentials().getPassword();
             if(!username || !*username || !password || !*password)
                 return false;
 
+            __int64 maxPWAge = getMaxPwdAge();
+            if (maxPWAge != (__int64)0x8000000000000000)
+                m_passwordNeverExpires = false;
+            else
+                m_passwordNeverExpires = true;
+
             const char* sysuser = m_ldapconfig->getSysUser();
             if(sysuser && *sysuser && (strcmp(username, sysuser) == 0))
             {
@@ -991,7 +1033,7 @@ public:
                 filter.append("uid=");
             filter.append(username);
 
-            char* attrs[] = {"cn", NULL};
+            char* attrs[] = {"cn", "pwdLastSet", "givenName", "sn", NULL};
 
             Owned<ILdapConnection> lconn = m_connections->getConnection();
             LDAP* sys_ld = ((CLdapConnection*)lconn.get())->getLd();
@@ -1046,11 +1088,70 @@ public:
             {
                 if((stricmp(attribute, "cn") == 0) && (values = ldap_get_values(sys_ld, entry, attribute)) != NULL )
                 {
-                    //set the FullName
                     if(values[0] != NULL)
                         user.setFullName(values[0]);
                     ldap_value_free( values );
-                    break;
+                }
+                else if((stricmp(attribute, "givenName") == 0) && (values = ldap_get_values(sys_ld, entry, attribute)) != NULL )
+                {
+                    if(values[0] != NULL)
+                        user.setFirstName(values[0]);
+                    ldap_value_free( values );
+                }
+                else if((stricmp(attribute, "sn") == 0) && (values = ldap_get_values(sys_ld, entry, attribute)) != NULL )
+                {
+                    if(values[0] != NULL)
+                        user.setLastName(values[0]);
+                    ldap_value_free( values );
+                }
+                else if((stricmp(attribute, "pwdLastSet") == 0) && (bvalues = ldap_get_values_len(sys_ld, entry, attribute)) != NULL )
+                {
+                    /*pwdLastSet is the date and time that the password for this account was last changed. This
+                      value is stored as a large integer that represents the number of 100 nanosecond intervals
+                      since January 1, 1601 (UTC), also known as a FILETIME value. If this value is set
+                      to 0 and the User-Account-Control attribute does not contain the UF_DONT_EXPIRE_PASSWD
+                      flag, then the user must set the password at the next logon.
+                      */
+                    if (!m_passwordNeverExpires)
+                    {
+                        CDateTime expiry;
+                        struct berval* val = bvalues[0];
+                        if(val != NULL)
+                        {
+                            __int64 time = 0;
+                            for (int x=0; x < (int)val->bv_len; x++)
+                                time = time * 10 + ( (int)val->bv_val[x] - '0');
+                            ldap_value_free_len(bvalues);
+#ifdef _DEBUG
+                            CDateTime lastPWChange;
+                            lastPWChange.setFromFILETIME(time);
+                            StringBuffer sb;
+                            lastPWChange.getString(sb);
+#endif
+                            time += maxPWAge;
+                            expiry.setFromFILETIME(time);
+                            user.setPasswordExpiration(expiry);
+#ifdef _DEBUG
+                            CDateTime whenExpires;
+                            whenExpires.setFromFILETIME(time);
+                            StringBuffer sb2;
+                            whenExpires.getString(sb2);
+#endif
+                        }
+                        else
+                        {
+                            DBGLOG("LDAP: Can't find entry for %s pwdLastSet", username);
+                            return false;
+                        }
+                    }
+                    else
+                    {
+                        CDateTime never;
+                        never.clear();
+                        assertex(never.isNull());
+                        user.setPasswordExpiration(never);
+                        DBGLOG("LDAP: Password never expires for user %s", username);
+                    }
                 }
             }
             ber_free(ber, 0);
@@ -2368,7 +2469,7 @@ public:
         }
         catch(IException*)
         {
-            throw MakeStringException(-1, "Failed to set user %s's password because of not being able to create an SSL conenction to the ldap server. To set an Active Directory user's password from Linux, you need to enable SSL on the Active Directory ldap server", username);
+            throw MakeStringException(-1, "Failed to set user %s's password because of not being able to create an SSL connection to the ldap server. To set an Active Directory user's password from Linux, you need to enable SSL on the Active Directory ldap server", username);
         }
 
         LDAP* ld = ((CLdapConnection*)lconn.get())->getLd();
@@ -2450,7 +2551,7 @@ public:
             StringBuffer errmsg;
             errmsg.appendf("Error setting password for %s - (%d) %s.", username, rc, ldap_err2string( rc ));
             if(rc == LDAP_UNWILLING_TO_PERFORM)
-                errmsg.append(" The ldap server refused to execute the password change action, one of the reasons might be that the new password you entered doesn't satisfy the policy requirement.");
+                errmsg.append(" The ldap server refused to change the password. Usually this is because your new password doesn't satisfy the domain policy.");
 
             throw MakeStringException(-1, "%s", errmsg.str());
         }
@@ -4711,7 +4812,8 @@ private:
         act_ctrl_val &= 0xFFFFFFFD;
         
         // UF_DONT_EXPIRE_PASSWD 0x10000
-        act_ctrl_val |= 0x10000;
+        if (m_passwordNeverExpires)
+            act_ctrl_val |= 0x10000;
 
         StringBuffer new_act_ctrl;
         new_act_ctrl.append(act_ctrl_val);

+ 1 - 0
system/security/LdapSecurity/ldapsecurity.cpp

@@ -210,6 +210,7 @@ void CLdapSecUser::copyTo(ISecUser& destination)
     dest->credentials().setPassword(credentials().getPassword());
     dest->setUserSid(m_usersid.length(), m_usersid.toByteArray());
     dest->setUserID(m_userid);
+    dest->setPasswordExpiration(m_passwordExpiration);
 }
 
 ISecUser * CLdapSecUser::clone()

+ 33 - 2
system/security/LdapSecurity/ldapsecurity.ipp

@@ -46,6 +46,7 @@ private:
     StringAttr   m_Fqdn;
     StringAttr   m_Peer;
     bool         m_isAuthenticated;
+    CDateTime    m_passwordExpiration;
     unsigned     m_userid;
     MemoryBuffer m_usersid;
     BufferArray  m_groupsids;
@@ -99,8 +100,38 @@ public:
     virtual bool setStatus(SecUserStatus Status){return false;}
 
 
-   virtual CDateTime& getPasswordExpiration(CDateTime& expirationDate) {return expirationDate; }
-   virtual bool setPasswordExpiration(CDateTime& expirationDate){return false;}
+   virtual CDateTime& getPasswordExpiration(CDateTime& expirationDate)
+   {
+       expirationDate.set(m_passwordExpiration);
+       return expirationDate;
+   }
+   virtual bool setPasswordExpiration(CDateTime& expirationDate)
+   {
+       m_passwordExpiration.set(expirationDate);
+       return true;
+   }
+   virtual int getPasswordDaysRemaining()
+   {
+       if (m_passwordExpiration.isNull())
+           return -2;//-2 if never expires
+       CDateTime expiry(m_passwordExpiration);
+       expiry.setTime(0,0,0,0);
+
+       CDateTime now;
+       now.setNow();
+       now.setTime(0,0,0,0);
+       if (expiry <= now)
+           return -1;//-1 if already expired
+
+       int numDays = -1;
+       while (expiry >= now)
+       {
+           ++numDays;
+           now.adjustTime(24*60);
+       }
+       return numDays;
+   }
+
    ISecUser * clone();
     virtual void setProperty(const char* name, const char* value){}
     virtual const char* getProperty(const char* name){ return "";}

+ 4 - 13
system/security/shared/SecureUser.hpp

@@ -39,8 +39,6 @@ private:
     unsigned        m_userID;
     StringBuffer    m_Fqdn;
     StringBuffer    m_Peer;
-    
-    CDateTime       m_PasswordExpirationDate;
     SecUserStatus   m_status;
     Owned<IProperties> m_parameters;
 
@@ -211,16 +209,9 @@ public:
         return m_userID;
     }
 
-    virtual CDateTime& getPasswordExpiration(CDateTime& expirationDate) 
-    {
-        expirationDate.set(m_PasswordExpirationDate);
-        return expirationDate; 
-    }
-    virtual bool setPasswordExpiration(CDateTime& expirationDate)
-    {
-        m_PasswordExpirationDate.set(expirationDate);
-        return true;
-    }
+    virtual CDateTime & getPasswordExpiration(CDateTime& expirationDate){ assertex(false); return expirationDate; }
+    virtual bool setPasswordExpiration(CDateTime& expirationDate) { assertex(false);return true; }
+    virtual int getPasswordDaysRemaining() {assertex(false);return -1;}
 
     virtual void copyTo(ISecUser& destination)
     {
@@ -236,7 +227,7 @@ public:
         CDateTime tmpTime;
         destination.setPasswordExpiration(getPasswordExpiration(tmpTime));
         destination.setStatus(getStatus());
-        
+
         if(m_parameters.get()==NULL)
             return;
         CriticalBlock b(crit);

+ 5 - 0
system/security/shared/seclib.hpp

@@ -20,6 +20,7 @@
 #define _SECLIB_HPP__
 
 #include "jlib.hpp"
+#include "jtime.hpp"
 #include "jexcept.hpp"
 
 #ifndef SECLIB_API
@@ -125,6 +126,9 @@ interface ISecCredentials : extends IInterface
     virtual bool setPassword(const char * pw) = 0;
     virtual const char * getPassword() = 0;
     virtual bool addToken(unsigned type, void * data, unsigned length) = 0;
+    virtual bool setPasswordExpiration(CDateTime & expirationDate) = 0;
+    virtual CDateTime & getPasswordExpiration(CDateTime & expirationDate) = 0;
+    virtual int getPasswordDaysRemaining() = 0;
 };
 
 
@@ -154,6 +158,7 @@ interface ISecUser : extends IInterface
     virtual void copyTo(ISecUser & destination) = 0;
     virtual CDateTime & getPasswordExpiration(CDateTime & expirationDate) = 0;
     virtual bool setPasswordExpiration(CDateTime & expirationDate) = 0;
+    virtual int getPasswordDaysRemaining() = 0;
     virtual void setProperty(const char * name, const char * value) = 0;
     virtual const char * getProperty(const char * name) = 0;
     virtual void setPropertyInt(const char * name, int value) = 0;