IDEMPIERE-4782 Multi-factor authentication (FHCA-2034) (#705)

* IDEMPIERE-4782 Multi-factor authentication (FHCA-2034)

Implement suggestions from Heng Sin

* IDEMPIERE-4782 Multi-factor authentication (FHCA-2034)

Fix security warning advised by github/CodeQL

* IDEMPIERE-4782 Multi-factor authentication (FHCA-2034)

Implement an incremental delay in zk when the validation code is wrong (to avoid brute-force attacks)
As suggested by Ricardo Santana:
* ensures one-time only use of an OTP
* Log failures in AuthFailure.log

* IDEMPIERE-4782 Multi-factor authentication (FHCA-2034)

* Log failures in AuthFailure.log - add case for login with email
* Implement incremental delay also for login panel
This commit is contained in:
Carlos Ruiz 2021-06-08 13:49:46 +02:00 committed by GitHub
parent 3e64dc6737
commit a4f67eb852
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 7576 additions and 87 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
SET SQLBLANKLINES ON
SET DEFINE OFF
-- IDEMPIERE-4782 Multi-factor authentication (FHCA-2034)
-- Jun 7, 2021, 9:39:32 PM CEST
INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,PrintName,EntityType,AD_Element_UU) VALUES (203515,0,0,'Y',TO_DATE('2021-06-07 21:39:32','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:39:32','YYYY-MM-DD HH24:MI:SS'),100,'MFALastSecret','Last MFA Secret','Last MFA Secret','D','e65db29f-25a3-4b07-acd3-1d442897e22f')
;
-- Jun 7, 2021, 9:02:33 PM CEST
INSERT INTO AD_Column (AD_Column_ID,Version,Name,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214500,0,'Last MFA Secret',200275,'MFALastSecret',2000,'N','N','N','N','N',0,'N',10,0,0,'Y',TO_DATE('2021-06-07 21:02:32','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:02:32','YYYY-MM-DD HH24:MI:SS'),100,203515,'Y','N','D','N','N','N','Y','13927461-61b7-4008-b867-25147cbc6eb3','Y',0,'N','N','N','N')
;
-- Jun 7, 2021, 9:02:59 PM CEST
INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203513,0,0,'Y',TO_DATE('2021-06-07 21:02:48','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:02:48','YYYY-MM-DD HH24:MI:SS'),100,'LastSuccess','Last Success',NULL,NULL,'Last Success','D','b9882967-1536-463b-ba6b-e185ea647e20')
;
-- Jun 7, 2021, 9:03:07 PM CEST
INSERT INTO AD_Column (AD_Column_ID,Version,Name,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214501,0,'Last Success',200275,'LastSuccess',7,'N','N','N','N','N',0,'N',16,0,0,'Y',TO_DATE('2021-06-07 21:03:07','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:03:07','YYYY-MM-DD HH24:MI:SS'),100,203513,'Y','N','D','N','N','N','Y','b71e7f11-90be-4596-8368-26fd0a0606af','Y',0,'N','N','N','N')
;
-- Jun 7, 2021, 9:03:32 PM CEST
ALTER TABLE MFA_Registration ADD MFALastSecret VARCHAR2(2000 CHAR) DEFAULT NULL
;
-- Jun 7, 2021, 9:03:56 PM CEST
ALTER TABLE MFA_Registration ADD LastSuccess DATE DEFAULT NULL
;
-- Jun 7, 2021, 9:04:16 PM CEST
INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203514,0,0,'Y',TO_DATE('2021-06-07 21:04:09','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:04:09','YYYY-MM-DD HH24:MI:SS'),100,'LastFailure','Last Failure',NULL,NULL,'Last Failure','D','d1f7a3ad-db3c-47a1-9d6a-67163d72a88d')
;
-- Jun 7, 2021, 9:04:20 PM CEST
INSERT INTO AD_Column (AD_Column_ID,Version,Name,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214502,0,'Last Failure',200275,'LastFailure',7,'N','N','N','N','N',0,'N',16,0,0,'Y',TO_DATE('2021-06-07 21:04:20','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:04:20','YYYY-MM-DD HH24:MI:SS'),100,203514,'Y','N','D','N','N','N','Y','3dff6b96-912a-405e-bcf4-662466d708f6','Y',0,'N','N','N','N')
;
-- Jun 7, 2021, 9:04:21 PM CEST
ALTER TABLE MFA_Registration ADD LastFailure DATE DEFAULT NULL
;
-- Jun 7, 2021, 9:04:56 PM CEST
INSERT INTO AD_Column (AD_Column_ID,Version,Name,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214503,0,'Failed Login Count',200275,'FailedLoginCount',10,'N','N','N','N','N',0,'N',11,0,0,'Y',TO_DATE('2021-06-07 21:04:56','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:04:56','YYYY-MM-DD HH24:MI:SS'),100,200113,'N','N','D','N','N','N','Y','ab6cf862-137c-4b10-939a-f97b19dc3943','Y',0,'N','N','N','N')
;
-- Jun 7, 2021, 9:04:58 PM CEST
ALTER TABLE MFA_Registration ADD FailedLoginCount NUMBER(10) DEFAULT NULL
;
-- Jun 7, 2021, 9:05:11 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (206666,'Last MFA Secret',200290,214500,'Y',2000,160,'N','N','N','N',0,0,'Y',TO_DATE('2021-06-07 21:05:11','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:05:11','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','f1754863-8ed2-468c-be3a-6ebff14996a7','Y',150,5)
;
-- Jun 7, 2021, 9:05:12 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (206667,'Last Success',200290,214501,'Y',7,170,'N','N','N','N',0,0,'Y',TO_DATE('2021-06-07 21:05:11','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:05:11','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','b2473190-ee58-47fb-b99a-d21242187b7f','Y',160,2)
;
-- Jun 7, 2021, 9:05:12 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (206668,'Last Failure',200290,214502,'Y',7,180,'N','N','N','N',0,0,'Y',TO_DATE('2021-06-07 21:05:12','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:05:12','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','57cce993-f54c-48c0-b130-384162ab73ea','Y',170,2)
;
-- Jun 7, 2021, 9:05:12 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (206669,'Failed Login Count',200290,214503,'Y',10,190,'N','N','N','N',0,0,'Y',TO_DATE('2021-06-07 21:05:12','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:05:12','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','d5738f08-cdfe-4417-97c1-875db62bf899','Y',180,2)
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET SeqNo=130, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206651
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET SeqNo=140, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206652
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET SeqNo=150, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, ColumnSpan=2, IsToolbarButton=NULL,Updated=TO_DATE('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206666
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=160, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=4, IsToolbarButton=NULL,Updated=TO_DATE('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206667
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET SeqNo=170, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206668
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=180, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=4, IsToolbarButton=NULL,Updated=TO_DATE('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206669
;
-- Jun 7, 2021, 9:58:42 PM CEST
INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','The one-time validation code has been used, please try again with a different code',0,0,'Y',TO_DATE('2021-06-07 21:58:41','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2021-06-07 21:58:41','YYYY-MM-DD HH24:MI:SS'),100,200710,'MFACodeAlreadyConsumed','D','a1c11929-d6ae-410b-a57a-eb9f6259fbee')
;
SELECT register_migration_script('202106072201_IDEMPIERE-4782.sql') FROM dual
;

View File

@ -0,0 +1,92 @@
-- IDEMPIERE-4782 Multi-factor authentication (FHCA-2034)
-- Jun 7, 2021, 9:39:32 PM CEST
INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,PrintName,EntityType,AD_Element_UU) VALUES (203515,0,0,'Y',TO_TIMESTAMP('2021-06-07 21:39:32','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:39:32','YYYY-MM-DD HH24:MI:SS'),100,'MFALastSecret','Last MFA Secret','Last MFA Secret','D','e65db29f-25a3-4b07-acd3-1d442897e22f')
;
-- Jun 7, 2021, 9:02:33 PM CEST
INSERT INTO AD_Column (AD_Column_ID,Version,Name,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214500,0,'Last MFA Secret',200275,'MFALastSecret',2000,'N','N','N','N','N',0,'N',10,0,0,'Y',TO_TIMESTAMP('2021-06-07 21:02:32','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:02:32','YYYY-MM-DD HH24:MI:SS'),100,203515,'Y','N','D','N','N','N','Y','13927461-61b7-4008-b867-25147cbc6eb3','Y',0,'N','N','N','N')
;
-- Jun 7, 2021, 9:02:59 PM CEST
INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203513,0,0,'Y',TO_TIMESTAMP('2021-06-07 21:02:48','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:02:48','YYYY-MM-DD HH24:MI:SS'),100,'LastSuccess','Last Success',NULL,NULL,'Last Success','D','b9882967-1536-463b-ba6b-e185ea647e20')
;
-- Jun 7, 2021, 9:03:07 PM CEST
INSERT INTO AD_Column (AD_Column_ID,Version,Name,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214501,0,'Last Success',200275,'LastSuccess',7,'N','N','N','N','N',0,'N',16,0,0,'Y',TO_TIMESTAMP('2021-06-07 21:03:07','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:03:07','YYYY-MM-DD HH24:MI:SS'),100,203513,'Y','N','D','N','N','N','Y','b71e7f11-90be-4596-8368-26fd0a0606af','Y',0,'N','N','N','N')
;
-- Jun 7, 2021, 9:03:32 PM CEST
ALTER TABLE MFA_Registration ADD COLUMN MFALastSecret VARCHAR(2000) DEFAULT NULL
;
-- Jun 7, 2021, 9:03:56 PM CEST
ALTER TABLE MFA_Registration ADD COLUMN LastSuccess TIMESTAMP DEFAULT NULL
;
-- Jun 7, 2021, 9:04:16 PM CEST
INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203514,0,0,'Y',TO_TIMESTAMP('2021-06-07 21:04:09','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:04:09','YYYY-MM-DD HH24:MI:SS'),100,'LastFailure','Last Failure',NULL,NULL,'Last Failure','D','d1f7a3ad-db3c-47a1-9d6a-67163d72a88d')
;
-- Jun 7, 2021, 9:04:20 PM CEST
INSERT INTO AD_Column (AD_Column_ID,Version,Name,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214502,0,'Last Failure',200275,'LastFailure',7,'N','N','N','N','N',0,'N',16,0,0,'Y',TO_TIMESTAMP('2021-06-07 21:04:20','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:04:20','YYYY-MM-DD HH24:MI:SS'),100,203514,'Y','N','D','N','N','N','Y','3dff6b96-912a-405e-bcf4-662466d708f6','Y',0,'N','N','N','N')
;
-- Jun 7, 2021, 9:04:21 PM CEST
ALTER TABLE MFA_Registration ADD COLUMN LastFailure TIMESTAMP DEFAULT NULL
;
-- Jun 7, 2021, 9:04:56 PM CEST
INSERT INTO AD_Column (AD_Column_ID,Version,Name,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214503,0,'Failed Login Count',200275,'FailedLoginCount',10,'N','N','N','N','N',0,'N',11,0,0,'Y',TO_TIMESTAMP('2021-06-07 21:04:56','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:04:56','YYYY-MM-DD HH24:MI:SS'),100,200113,'N','N','D','N','N','N','Y','ab6cf862-137c-4b10-939a-f97b19dc3943','Y',0,'N','N','N','N')
;
-- Jun 7, 2021, 9:04:58 PM CEST
ALTER TABLE MFA_Registration ADD COLUMN FailedLoginCount NUMERIC(10) DEFAULT NULL
;
-- Jun 7, 2021, 9:05:11 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (206666,'Last MFA Secret',200290,214500,'Y',2000,160,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2021-06-07 21:05:11','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:05:11','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','f1754863-8ed2-468c-be3a-6ebff14996a7','Y',150,5)
;
-- Jun 7, 2021, 9:05:12 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (206667,'Last Success',200290,214501,'Y',7,170,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2021-06-07 21:05:11','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:05:11','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','b2473190-ee58-47fb-b99a-d21242187b7f','Y',160,2)
;
-- Jun 7, 2021, 9:05:12 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (206668,'Last Failure',200290,214502,'Y',7,180,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2021-06-07 21:05:12','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:05:12','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','57cce993-f54c-48c0-b130-384162ab73ea','Y',170,2)
;
-- Jun 7, 2021, 9:05:12 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (206669,'Failed Login Count',200290,214503,'Y',10,190,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2021-06-07 21:05:12','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:05:12','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','d5738f08-cdfe-4417-97c1-875db62bf899','Y',180,2)
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET SeqNo=130, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206651
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET SeqNo=140, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206652
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET SeqNo=150, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, ColumnSpan=2, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206666
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=160, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=4, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206667
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET SeqNo=170, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206668
;
-- Jun 7, 2021, 9:05:50 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=180, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=4, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2021-06-07 21:05:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206669
;
-- Jun 7, 2021, 9:58:42 PM CEST
INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','The one-time validation code has been used, please try again with a different code',0,0,'Y',TO_TIMESTAMP('2021-06-07 21:58:41','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2021-06-07 21:58:41','YYYY-MM-DD HH24:MI:SS'),100,200710,'MFACodeAlreadyConsumed','D','a1c11929-d6ae-410b-a57a-eb9f6259fbee')
;
SELECT register_migration_script('202106072201_IDEMPIERE-4782.sql') FROM dual
;

View File

@ -90,6 +90,8 @@
<setEntry value="slf4j.api@default:default"/>
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>

View File

@ -107,6 +107,8 @@
<setEntry value="slf4j.api@default:default"/>
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>

View File

@ -104,6 +104,8 @@
<setEntry value="slf4j.api@default:default"/>
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>

View File

@ -105,6 +105,8 @@
<setEntry value="slf4j.api@default:default"/>
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>

View File

@ -104,6 +104,8 @@
<setEntry value="slf4j.api@default:default"/>
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>

View File

@ -30,6 +30,11 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ds.core.builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>

View File

@ -151,6 +151,8 @@ Require-Bundle: org.eclipse.equinox.app;bundle-version="0.0.0",
wrapped.com.google.http-client.google-http-client-gson;bundle-version="1.38.1",
org.apache.httpcomponents.httpclient;bundle-version="4.5.10",
org.apache.httpcomponents.httpcore;bundle-version="4.4.12",
com.google.guava;bundle-version="28.2.0"
com.google.guava;bundle-version="28.2.0",
wrapped.com.google.zxing.javase;bundle-version="3.4.1",
wrapped.dev.samstevens.totp.totp;bundle-version="1.7.1"
Automatic-Module-Name: org.adempiere.base
Bundle-Vendor: iDempiere Community

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.idempiere.mfa.EMailMechanism">
<implementation class="org.idempiere.mfa.EMailMechanism"/>
<service>
<provide interface="org.compiere.model.IMFAMechanism"/>
</service>
<property name="method" type="String" value="EMail"/>
</scr:component>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.idempiere.mfa.TOTPMechanism">
<implementation class="org.idempiere.mfa.TOTPMechanism"/>
<service>
<provide interface="org.compiere.model.IMFAMechanism"/>
</service>
<property name="method" type="String" value="TOTP"/>
</scr:component>

View File

@ -1,20 +1,5 @@
bin.includes = plugin.xml,\
OSGI-INF/,\
OSGI-INF/dslocator.xml,\
OSGI-INF/defaultmodelfactory.xml,\
OSGI-INF/defaultdocfactory.xml,\
OSGI-INF/defaultcolumncalloutfactory.xml,\
OSGI-INF/defaultmodelvalidatorfactory.xml,\
OSGI-INF/defaultprocessfactory.xml,\
OSGI-INF/defaultshipmentprocessorfactory.xml,\
OSGI-INF/defaultpaymentprocessorfactory.xml,\
OSGI-INF/broadcastutil.xml,\
OSGI-INF/requesteventhandler.xml,\
OSGI-INF/requestpropertyservice.xml,\
OSGI-INF/defaultaddressvalidationfactory.xml,\
OSGI-INF/defaulttaxproviderfactory.xml,\
OSGI-INF/addressvalidationeventhandler.xml,\
OSGI-INF/defaultproductpricingfactory.xml,\
schema/,\
.,\
META-INF/,\

View File

@ -0,0 +1,76 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - Carlos Ruiz (sponsored by FH) *
**********************************************************************/
package org.compiere.model;
import java.util.Properties;
public interface IMFAMechanism {
/**
* Registration mechanism for the method
* Here the registration method executes the actions expected for this method, like sending an email, or an SMS, or nothing
* and creates the registration record
* @param ctx
* @param method
* @param prm optional, for example the email
* @param trxName
* @return Object[] - first object is the String with the instructions to follow
* second object is the registration generated
* third and posterior objects are optional additional information for the method
* like QRCode image for example, or html img object, or URL, or File
*/
Object[] register(Properties ctx, MMFAMethod method, String prm, String trxName);
/**
* Complete/Validate a previous registration
* Here it must check for validity of the mechanism, mark the record as valid or throw exception when not
* @param ctx
* @param reg The registration object
* @param code The code to be validated
* @param name Optional - a name to assign the registration
* @param preferred
* @param trxName
* @return msg A message indicating success, errors throw exception
*/
String complete(Properties ctx, MMFARegistration reg, String code, String name, boolean preferred, String trxName);
/**
* Generate a validation code (when needed depending on the method)
* @param reg
* @return
*/
String generateValidationCode(MMFARegistration reg);
/**
* Validate a code
* @param reg
* @param code
* @param setPreferred
* @return message on error, null when OK
*/
String validateCode(MMFARegistration reg, String code, boolean setPreferred);
}

View File

@ -0,0 +1,263 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2012 ComPiere, Inc. All Rights Reserved. *
* This program is free software, you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program, if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.compiere.model;
import java.math.BigDecimal;
import java.sql.Timestamp;
import org.compiere.util.KeyNamePair;
/** Generated Interface for MFA_Method
* @author iDempiere (generated)
* @version Release 8.2
*/
public interface I_MFA_Method
{
/** TableName=MFA_Method */
public static final String Table_Name = "MFA_Method";
/** AD_Table_ID=200273 */
public static final int Table_ID = 200273;
KeyNamePair Model = new KeyNamePair(Table_ID, Table_Name);
/** AccessLevel = 4 - System
*/
BigDecimal accessLevel = BigDecimal.valueOf(4);
/** Load Meta Data */
/** Column name AD_Client_ID */
public static final String COLUMNNAME_AD_Client_ID = "AD_Client_ID";
/** Get Client.
* Client/Tenant for this installation.
*/
public int getAD_Client_ID();
/** Column name AD_Org_ID */
public static final String COLUMNNAME_AD_Org_ID = "AD_Org_ID";
/** Set Organization.
* Organizational entity within client
*/
public void setAD_Org_ID (int AD_Org_ID);
/** Get Organization.
* Organizational entity within client
*/
public int getAD_Org_ID();
/** Column name Created */
public static final String COLUMNNAME_Created = "Created";
/** Get Created.
* Date this record was created
*/
public Timestamp getCreated();
/** Column name CreatedBy */
public static final String COLUMNNAME_CreatedBy = "CreatedBy";
/** Get Created By.
* User who created this records
*/
public int getCreatedBy();
/** Column name Description */
public static final String COLUMNNAME_Description = "Description";
/** Set Description.
* Optional short description of the record
*/
public void setDescription (String Description);
/** Get Description.
* Optional short description of the record
*/
public String getDescription();
/** Column name ExpireInMinutes */
public static final String COLUMNNAME_ExpireInMinutes = "ExpireInMinutes";
/** Set Expire in Minutes */
public void setExpireInMinutes (int ExpireInMinutes);
/** Get Expire in Minutes */
public int getExpireInMinutes();
/** Column name Help */
public static final String COLUMNNAME_Help = "Help";
/** Set Comment/Help.
* Comment or Hint
*/
public void setHelp (String Help);
/** Get Comment/Help.
* Comment or Hint
*/
public String getHelp();
/** Column name IsActive */
public static final String COLUMNNAME_IsActive = "IsActive";
/** Set Active.
* The record is active in the system
*/
public void setIsActive (boolean IsActive);
/** Get Active.
* The record is active in the system
*/
public boolean isActive();
/** Column name Method */
public static final String COLUMNNAME_Method = "Method";
/** Set Method */
public void setMethod (String Method);
/** Get Method */
public String getMethod();
/** Column name MFAAllowedTimeDiscrepancy */
public static final String COLUMNNAME_MFAAllowedTimeDiscrepancy = "MFAAllowedTimeDiscrepancy";
/** Set Allowed Time Period Discrepancy */
public void setMFAAllowedTimeDiscrepancy (int MFAAllowedTimeDiscrepancy);
/** Get Allowed Time Period Discrepancy */
public int getMFAAllowedTimeDiscrepancy();
/** Column name MFA_ElementPrm_ID */
public static final String COLUMNNAME_MFA_ElementPrm_ID = "MFA_ElementPrm_ID";
/** Set Parameter Element */
public void setMFA_ElementPrm_ID (int MFA_ElementPrm_ID);
/** Get Parameter Element */
public int getMFA_ElementPrm_ID();
public org.compiere.model.I_AD_Element getMFA_ElementPrm() throws RuntimeException;
/** Column name MFAIssuer */
public static final String COLUMNNAME_MFAIssuer = "MFAIssuer";
/** Set Issuer */
public void setMFAIssuer (String MFAIssuer);
/** Get Issuer */
public String getMFAIssuer();
/** Column name MFA_Method_ID */
public static final String COLUMNNAME_MFA_Method_ID = "MFA_Method_ID";
/** Set MFA Method.
* Multi-factor Authentication Method
*/
public void setMFA_Method_ID (int MFA_Method_ID);
/** Get MFA Method.
* Multi-factor Authentication Method
*/
public int getMFA_Method_ID();
/** Column name MFA_Method_UU */
public static final String COLUMNNAME_MFA_Method_UU = "MFA_Method_UU";
/** Set MFA_Method_UU */
public void setMFA_Method_UU (String MFA_Method_UU);
/** Get MFA_Method_UU */
public String getMFA_Method_UU();
/** Column name MFATimeProvider */
public static final String COLUMNNAME_MFATimeProvider = "MFATimeProvider";
/** Set Time Provider */
public void setMFATimeProvider (String MFATimeProvider);
/** Get Time Provider */
public String getMFATimeProvider();
/** Column name MFATimeServer */
public static final String COLUMNNAME_MFATimeServer = "MFATimeServer";
/** Set Time Server */
public void setMFATimeServer (String MFATimeServer);
/** Get Time Server */
public String getMFATimeServer();
/** Column name MFAType */
public static final String COLUMNNAME_MFAType = "MFAType";
/** Set MFA Type.
* Multi-factor authentication type (Something you Know/Have/Are, Location)
*/
public void setMFAType (String MFAType);
/** Get MFA Type.
* Multi-factor authentication type (Something you Know/Have/Are, Location)
*/
public String getMFAType();
/** Column name Name */
public static final String COLUMNNAME_Name = "Name";
/** Set Name.
* Alphanumeric identifier of the entity
*/
public void setName (String Name);
/** Get Name.
* Alphanumeric identifier of the entity
*/
public String getName();
/** Column name R_MailText_ID */
public static final String COLUMNNAME_R_MailText_ID = "R_MailText_ID";
/** Set Mail Template.
* Text templates for mailings
*/
public void setR_MailText_ID (int R_MailText_ID);
/** Get Mail Template.
* Text templates for mailings
*/
public int getR_MailText_ID();
public org.compiere.model.I_R_MailText getR_MailText() throws RuntimeException;
/** Column name Updated */
public static final String COLUMNNAME_Updated = "Updated";
/** Get Updated.
* Date this record was updated
*/
public Timestamp getUpdated();
/** Column name UpdatedBy */
public static final String COLUMNNAME_UpdatedBy = "UpdatedBy";
/** Get Updated By.
* User who updated this records
*/
public int getUpdatedBy();
}

View File

@ -0,0 +1,181 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2012 ComPiere, Inc. All Rights Reserved. *
* This program is free software, you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program, if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.compiere.model;
import java.math.BigDecimal;
import java.sql.Timestamp;
import org.compiere.util.KeyNamePair;
/** Generated Interface for MFA_RegisteredDevice
* @author iDempiere (generated)
* @version Release 8.2
*/
public interface I_MFA_RegisteredDevice
{
/** TableName=MFA_RegisteredDevice */
public static final String Table_Name = "MFA_RegisteredDevice";
/** AD_Table_ID=200274 */
public static final int Table_ID = 200274;
KeyNamePair Model = new KeyNamePair(Table_ID, Table_Name);
/** AccessLevel = 6 - System - Client
*/
BigDecimal accessLevel = BigDecimal.valueOf(6);
/** Load Meta Data */
/** Column name AD_Client_ID */
public static final String COLUMNNAME_AD_Client_ID = "AD_Client_ID";
/** Get Client.
* Client/Tenant for this installation.
*/
public int getAD_Client_ID();
/** Column name AD_Org_ID */
public static final String COLUMNNAME_AD_Org_ID = "AD_Org_ID";
/** Set Organization.
* Organizational entity within client
*/
public void setAD_Org_ID (int AD_Org_ID);
/** Get Organization.
* Organizational entity within client
*/
public int getAD_Org_ID();
/** Column name AD_User_ID */
public static final String COLUMNNAME_AD_User_ID = "AD_User_ID";
/** Set User/Contact.
* User within the system - Internal or Business Partner Contact
*/
public void setAD_User_ID (int AD_User_ID);
/** Get User/Contact.
* User within the system - Internal or Business Partner Contact
*/
public int getAD_User_ID();
public org.compiere.model.I_AD_User getAD_User() throws RuntimeException;
/** Column name Created */
public static final String COLUMNNAME_Created = "Created";
/** Get Created.
* Date this record was created
*/
public Timestamp getCreated();
/** Column name CreatedBy */
public static final String COLUMNNAME_CreatedBy = "CreatedBy";
/** Get Created By.
* User who created this records
*/
public int getCreatedBy();
/** Column name Expiration */
public static final String COLUMNNAME_Expiration = "Expiration";
/** Set Expire On.
* Expire On
*/
public void setExpiration (Timestamp Expiration);
/** Get Expire On.
* Expire On
*/
public Timestamp getExpiration();
/** Column name Help */
public static final String COLUMNNAME_Help = "Help";
/** Set Comment/Help.
* Comment or Hint
*/
public void setHelp (String Help);
/** Get Comment/Help.
* Comment or Hint
*/
public String getHelp();
/** Column name IsActive */
public static final String COLUMNNAME_IsActive = "IsActive";
/** Set Active.
* The record is active in the system
*/
public void setIsActive (boolean IsActive);
/** Get Active.
* The record is active in the system
*/
public boolean isActive();
/** Column name MFADeviceIdentifier */
public static final String COLUMNNAME_MFADeviceIdentifier = "MFADeviceIdentifier";
/** Set MFA Device Identifier.
* Multi-factor Authentication Device Identifier
*/
public void setMFADeviceIdentifier (String MFADeviceIdentifier);
/** Get MFA Device Identifier.
* Multi-factor Authentication Device Identifier
*/
public String getMFADeviceIdentifier();
/** Column name MFA_RegisteredDevice_ID */
public static final String COLUMNNAME_MFA_RegisteredDevice_ID = "MFA_RegisteredDevice_ID";
/** Set MFA Registered Device */
public void setMFA_RegisteredDevice_ID (int MFA_RegisteredDevice_ID);
/** Get MFA Registered Device */
public int getMFA_RegisteredDevice_ID();
/** Column name MFA_RegisteredDevice_UU */
public static final String COLUMNNAME_MFA_RegisteredDevice_UU = "MFA_RegisteredDevice_UU";
/** Set MFA_RegisteredDevice_UU */
public void setMFA_RegisteredDevice_UU (String MFA_RegisteredDevice_UU);
/** Get MFA_RegisteredDevice_UU */
public String getMFA_RegisteredDevice_UU();
/** Column name Updated */
public static final String COLUMNNAME_Updated = "Updated";
/** Get Updated.
* Date this record was updated
*/
public Timestamp getUpdated();
/** Column name UpdatedBy */
public static final String COLUMNNAME_UpdatedBy = "UpdatedBy";
/** Get Updated By.
* User who updated this records
*/
public int getUpdatedBy();
}

View File

@ -0,0 +1,294 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2012 ComPiere, Inc. All Rights Reserved. *
* This program is free software, you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program, if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.compiere.model;
import java.math.BigDecimal;
import java.sql.Timestamp;
import org.compiere.util.KeyNamePair;
/** Generated Interface for MFA_Registration
* @author iDempiere (generated)
* @version Release 8.2
*/
public interface I_MFA_Registration
{
/** TableName=MFA_Registration */
public static final String Table_Name = "MFA_Registration";
/** AD_Table_ID=200275 */
public static final int Table_ID = 200275;
KeyNamePair Model = new KeyNamePair(Table_ID, Table_Name);
/** AccessLevel = 6 - System - Client
*/
BigDecimal accessLevel = BigDecimal.valueOf(6);
/** Load Meta Data */
/** Column name AD_Client_ID */
public static final String COLUMNNAME_AD_Client_ID = "AD_Client_ID";
/** Get Client.
* Client/Tenant for this installation.
*/
public int getAD_Client_ID();
/** Column name AD_Org_ID */
public static final String COLUMNNAME_AD_Org_ID = "AD_Org_ID";
/** Set Organization.
* Organizational entity within client
*/
public void setAD_Org_ID (int AD_Org_ID);
/** Get Organization.
* Organizational entity within client
*/
public int getAD_Org_ID();
/** Column name AD_User_ID */
public static final String COLUMNNAME_AD_User_ID = "AD_User_ID";
/** Set User/Contact.
* User within the system - Internal or Business Partner Contact
*/
public void setAD_User_ID (int AD_User_ID);
/** Get User/Contact.
* User within the system - Internal or Business Partner Contact
*/
public int getAD_User_ID();
public org.compiere.model.I_AD_User getAD_User() throws RuntimeException;
/** Column name Created */
public static final String COLUMNNAME_Created = "Created";
/** Get Created.
* Date this record was created
*/
public Timestamp getCreated();
/** Column name CreatedBy */
public static final String COLUMNNAME_CreatedBy = "CreatedBy";
/** Get Created By.
* User who created this records
*/
public int getCreatedBy();
/** Column name Expiration */
public static final String COLUMNNAME_Expiration = "Expiration";
/** Set Expire On.
* Expire On
*/
public void setExpiration (Timestamp Expiration);
/** Get Expire On.
* Expire On
*/
public Timestamp getExpiration();
/** Column name FailedLoginCount */
public static final String COLUMNNAME_FailedLoginCount = "FailedLoginCount";
/** Set Failed Login Count */
public void setFailedLoginCount (int FailedLoginCount);
/** Get Failed Login Count */
public int getFailedLoginCount();
/** Column name Help */
public static final String COLUMNNAME_Help = "Help";
/** Set Comment/Help.
* Comment or Hint
*/
public void setHelp (String Help);
/** Get Comment/Help.
* Comment or Hint
*/
public String getHelp();
/** Column name IsActive */
public static final String COLUMNNAME_IsActive = "IsActive";
/** Set Active.
* The record is active in the system
*/
public void setIsActive (boolean IsActive);
/** Get Active.
* The record is active in the system
*/
public boolean isActive();
/** Column name IsUserMFAPreferred */
public static final String COLUMNNAME_IsUserMFAPreferred = "IsUserMFAPreferred";
/** Set Preferred */
public void setIsUserMFAPreferred (boolean IsUserMFAPreferred);
/** Get Preferred */
public boolean isUserMFAPreferred();
/** Column name IsValid */
public static final String COLUMNNAME_IsValid = "IsValid";
/** Set Valid.
* Element is valid
*/
public void setIsValid (boolean IsValid);
/** Get Valid.
* Element is valid
*/
public boolean isValid();
/** Column name LastFailure */
public static final String COLUMNNAME_LastFailure = "LastFailure";
/** Set Last Failure */
public void setLastFailure (Timestamp LastFailure);
/** Get Last Failure */
public Timestamp getLastFailure();
/** Column name LastSuccess */
public static final String COLUMNNAME_LastSuccess = "LastSuccess";
/** Set Last Success */
public void setLastSuccess (Timestamp LastSuccess);
/** Get Last Success */
public Timestamp getLastSuccess();
/** Column name MFALastSecret */
public static final String COLUMNNAME_MFALastSecret = "MFALastSecret";
/** Set Last MFA Secret */
public void setMFALastSecret (String MFALastSecret);
/** Get Last MFA Secret */
public String getMFALastSecret();
/** Column name MFA_Method_ID */
public static final String COLUMNNAME_MFA_Method_ID = "MFA_Method_ID";
/** Set MFA Method.
* Multi-factor Authentication Method
*/
public void setMFA_Method_ID (int MFA_Method_ID);
/** Get MFA Method.
* Multi-factor Authentication Method
*/
public int getMFA_Method_ID();
public org.compiere.model.I_MFA_Method getMFA_Method() throws RuntimeException;
/** Column name MFA_Registration_ID */
public static final String COLUMNNAME_MFA_Registration_ID = "MFA_Registration_ID";
/** Set MFA Registration */
public void setMFA_Registration_ID (int MFA_Registration_ID);
/** Get MFA Registration */
public int getMFA_Registration_ID();
/** Column name MFA_Registration_UU */
public static final String COLUMNNAME_MFA_Registration_UU = "MFA_Registration_UU";
/** Set MFA_Registration_UU */
public void setMFA_Registration_UU (String MFA_Registration_UU);
/** Get MFA_Registration_UU */
public String getMFA_Registration_UU();
/** Column name MFASecret */
public static final String COLUMNNAME_MFASecret = "MFASecret";
/** Set MFA Secret.
* Multi-factor Authentication Secret
*/
public void setMFASecret (String MFASecret);
/** Get MFA Secret.
* Multi-factor Authentication Secret
*/
public String getMFASecret();
/** Column name MFAUnregisteredAt */
public static final String COLUMNNAME_MFAUnregisteredAt = "MFAUnregisteredAt";
/** Set Unregistered at */
public void setMFAUnregisteredAt (Timestamp MFAUnregisteredAt);
/** Get Unregistered at */
public Timestamp getMFAUnregisteredAt();
/** Column name MFAValidatedAt */
public static final String COLUMNNAME_MFAValidatedAt = "MFAValidatedAt";
/** Set Validated at */
public void setMFAValidatedAt (Timestamp MFAValidatedAt);
/** Get Validated at */
public Timestamp getMFAValidatedAt();
/** Column name Name */
public static final String COLUMNNAME_Name = "Name";
/** Set Name.
* Alphanumeric identifier of the entity
*/
public void setName (String Name);
/** Get Name.
* Alphanumeric identifier of the entity
*/
public String getName();
/** Column name ParameterValue */
public static final String COLUMNNAME_ParameterValue = "ParameterValue";
/** Set Parameter Value */
public void setParameterValue (String ParameterValue);
/** Get Parameter Value */
public String getParameterValue();
/** Column name Updated */
public static final String COLUMNNAME_Updated = "Updated";
/** Get Updated.
* Date this record was updated
*/
public Timestamp getUpdated();
/** Column name UpdatedBy */
public static final String COLUMNNAME_UpdatedBy = "UpdatedBy";
/** Get Updated By.
* User who updated this records
*/
public int getUpdatedBy();
}

View File

@ -0,0 +1,159 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2012 ComPiere, Inc. All Rights Reserved. *
* This program is free software, you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program, if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.compiere.model;
import java.math.BigDecimal;
import java.sql.Timestamp;
import org.compiere.util.KeyNamePair;
/** Generated Interface for MFA_Rule
* @author iDempiere (generated)
* @version Release 8.2
*/
public interface I_MFA_Rule
{
/** TableName=MFA_Rule */
public static final String Table_Name = "MFA_Rule";
/** AD_Table_ID=200276 */
public static final int Table_ID = 200276;
KeyNamePair Model = new KeyNamePair(Table_ID, Table_Name);
/** AccessLevel = 6 - System - Client
*/
BigDecimal accessLevel = BigDecimal.valueOf(6);
/** Load Meta Data */
/** Column name AD_Client_ID */
public static final String COLUMNNAME_AD_Client_ID = "AD_Client_ID";
/** Get Client.
* Client/Tenant for this installation.
*/
public int getAD_Client_ID();
/** Column name AD_Org_ID */
public static final String COLUMNNAME_AD_Org_ID = "AD_Org_ID";
/** Set Organization.
* Organizational entity within client
*/
public void setAD_Org_ID (int AD_Org_ID);
/** Get Organization.
* Organizational entity within client
*/
public int getAD_Org_ID();
/** Column name Created */
public static final String COLUMNNAME_Created = "Created";
/** Get Created.
* Date this record was created
*/
public Timestamp getCreated();
/** Column name CreatedBy */
public static final String COLUMNNAME_CreatedBy = "CreatedBy";
/** Get Created By.
* User who created this records
*/
public int getCreatedBy();
/** Column name Help */
public static final String COLUMNNAME_Help = "Help";
/** Set Comment/Help.
* Comment or Hint
*/
public void setHelp (String Help);
/** Get Comment/Help.
* Comment or Hint
*/
public String getHelp();
/** Column name IsActive */
public static final String COLUMNNAME_IsActive = "IsActive";
/** Set Active.
* The record is active in the system
*/
public void setIsActive (boolean IsActive);
/** Get Active.
* The record is active in the system
*/
public boolean isActive();
/** Column name MFA_Method_ID */
public static final String COLUMNNAME_MFA_Method_ID = "MFA_Method_ID";
/** Set MFA Method.
* Multi-factor Authentication Method
*/
public void setMFA_Method_ID (int MFA_Method_ID);
/** Get MFA Method.
* Multi-factor Authentication Method
*/
public int getMFA_Method_ID();
public org.compiere.model.I_MFA_Method getMFA_Method() throws RuntimeException;
/** Column name MFA_Rule_ID */
public static final String COLUMNNAME_MFA_Rule_ID = "MFA_Rule_ID";
/** Set MFA Rule.
* Multi-factor Authentication Rule
*/
public void setMFA_Rule_ID (int MFA_Rule_ID);
/** Get MFA Rule.
* Multi-factor Authentication Rule
*/
public int getMFA_Rule_ID();
/** Column name MFA_Rule_UU */
public static final String COLUMNNAME_MFA_Rule_UU = "MFA_Rule_UU";
/** Set MFA_Rule_UU */
public void setMFA_Rule_UU (String MFA_Rule_UU);
/** Get MFA_Rule_UU */
public String getMFA_Rule_UU();
/** Column name Updated */
public static final String COLUMNNAME_Updated = "Updated";
/** Get Updated.
* Date this record was updated
*/
public Timestamp getUpdated();
/** Column name UpdatedBy */
public static final String COLUMNNAME_UpdatedBy = "UpdatedBy";
/** Get Updated By.
* User who updated this records
*/
public int getUpdatedBy();
}

View File

@ -0,0 +1,110 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - Carlos Ruiz (sponsored by FH) *
**********************************************************************/
package org.compiere.model;
import java.sql.ResultSet;
import java.util.Properties;
import org.adempiere.base.IServiceReferenceHolder;
import org.adempiere.base.Service;
import org.adempiere.base.ServiceQuery;
import org.adempiere.exceptions.AdempiereException;
import org.compiere.util.CCache;
/**
* Multi-factor Authentication Method
*/
public class MMFAMethod extends X_MFA_Method {
/**
*
*/
private static final long serialVersionUID = -7954271872310037840L;
/**
* Read/Create empty MFA Method
*
* @param ctx context
* @param MFA_Method_ID ID
* @param trxName transaction
*/
public MMFAMethod(Properties ctx, int MFA_Method_ID, String trxName) {
super(ctx, MFA_Method_ID, trxName);
} // MMFAMethod
/**
* Read MFA Method from current row in ResultSet
*
* @param ctx context
* @param rs ResultSet
* @param trxName transaction
*/
public MMFAMethod(Properties ctx, ResultSet rs, String trxName) {
super(ctx, rs, trxName);
} // MMFAMethod
/**
*
* @return {@link IMFAMechanism}
*/
public IMFAMechanism getMFAMechanism() {
ServiceQuery query = new ServiceQuery();
String method = getMethod();
if (method == null)
throw new AdempiereException("No method");
query.put("method", method);
IMFAMechanism mechanism = getMFAMechanismService(query);
if (mechanism == null)
throw new AdempiereException("No MFA mechanism provider found");
return mechanism;
}
private static CCache<ServiceQuery, IServiceReferenceHolder<IMFAMechanism>> s_MFAMechanismReference = new CCache<>(null, "IMFAMechanism", 3, false);
/**
*
* @param query
* @return {@link IMFAMechanism}
*/
public static IMFAMechanism getMFAMechanismService(ServiceQuery query) {
IMFAMechanism mechanism = null;
IServiceReferenceHolder<IMFAMechanism> cache = s_MFAMechanismReference.get(query);
if (cache != null) {
mechanism = cache.getService();
if (mechanism != null)
return mechanism;
else
s_MFAMechanismReference.remove(query);
}
IServiceReferenceHolder<IMFAMechanism> serviceReference = Service.locator().locate(IMFAMechanism.class, query).getServiceReference();
if (serviceReference != null) {
mechanism = serviceReference.getService();
if (mechanism != null)
s_MFAMechanismReference.put(query, serviceReference);
}
return mechanism;
}
} // MMFAMethod

View File

@ -0,0 +1,94 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - Carlos Ruiz (sponsored by FH) *
**********************************************************************/
package org.compiere.model;
import java.sql.ResultSet;
import java.util.Properties;
import org.compiere.util.Env;
/**
* Multi-factor Authentication Registered Device
*/
public class MMFARegisteredDevice extends X_MFA_RegisteredDevice {
/**
*
*/
private static final long serialVersionUID = 7913538709234444407L;
/**
* Read/Create empty MFA Registered Device
*
* @param ctx context
* @param MFA_RegisteredDevice_ID ID
* @param trxName transaction
*/
public MMFARegisteredDevice(Properties ctx, int MFA_RegisteredDevice_ID, String trxName) {
super(ctx, MFA_RegisteredDevice_ID, trxName);
} // MMFARegisteredDevice
/**
* Read MFA Registered Device from current row in ResultSet
*
* @param ctx context
* @param rs ResultSet
* @param trxName transaction
*/
public MMFARegisteredDevice(Properties ctx, ResultSet rs, String trxName) {
super(ctx, rs, trxName);
} // MMFARegisteredDevice
/**
* Validate if there is a non-expired device registered with that code
* @param registerCookie
* @return true if device is valid
*/
public static boolean isValid(String identifier) {
final String where = "AD_User_ID=? AND MFADeviceIdentifier=? AND Expiration>SYSDATE";
MMFARegisteredDevice rd = new Query(Env.getCtx(), Table_Name, where, null)
.setParameters(Env.getAD_User_ID(Env.getCtx()), identifier)
.setClient_ID()
.setOnlyActiveRecords(true)
.first();
return (rd != null);
}
/**
* Set User/Contact.
* @param AD_User_ID
* User within the system - Internal or Business Partner Contact
* Overridden to allow saving System record (zero ID)
*/
@Override
public void setAD_User_ID (int AD_User_ID)
{
if (AD_User_ID == 0)
set_ValueNoCheck (COLUMNNAME_AD_User_ID, AD_User_ID);
else
super.setAD_User_ID(AD_User_ID);
} //setAD_User_ID
} // MMFARegisteredDevice

View File

@ -0,0 +1,208 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - Carlos Ruiz (sponsored by FH) *
**********************************************************************/
package org.compiere.model;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.compiere.util.DB;
import org.compiere.util.Env;
/**
* Multi-factor Authentication Registration
*/
public class MMFARegistration extends X_MFA_Registration {
/**
*
*/
private static final long serialVersionUID = -2032862057961778934L;
/**
* Read/Create empty MFA Registration
*
* @param ctx context
* @param MFA_Registration_ID ID
* @param trxName transaction
*/
public MMFARegistration(Properties ctx, int MFA_Registration_ID, String trxName) {
super(ctx, MFA_Registration_ID, trxName);
} // MMFARegistration
/**
* Read MFA Registration from current row in ResultSet
*
* @param ctx context
* @param rs ResultSet
* @param trxName transaction
*/
public MMFARegistration(Properties ctx, ResultSet rs, String trxName) {
super(ctx, rs, trxName);
} // MMFARegistration
/**
* Validate if a method is already registered for this user
* @param method
* @param prm
* @return
*/
public static boolean alreadyExistsValid(MMFAMethod method, String prm) {
List<Object> params = new ArrayList<Object>();
params.add(Env.getAD_User_ID(method.getCtx()));
params.add(method.getMFA_Method_ID());
params.add(Env.getAD_Client_ID(method.getCtx()));
StringBuilder sql = new StringBuilder();
sql.append("SELECT COUNT(*)"
+ " FROM MFA_Registration"
+ " WHERE AD_User_ID=?"
+ " AND MFA_Method_ID=?"
+ " AND IsValid='Y'"
+ " AND AD_Client_ID=?"
+ " AND IsActive='Y'");
if (prm != null) {
sql.append(" AND ParameterValue=?");
params.add(prm);
}
int cnt = DB.getSQLValueEx(method.get_TrxName(), sql.toString(), params);
return cnt != 0;
}
public static void invalidatePreviousPending(MMFAMethod method, String prm, MMFARegistration reg) {
List<Object> params = new ArrayList<Object>();
params.add(Env.getAD_User_ID(method.getCtx()));
params.add(method.getMFA_Method_ID());
params.add(Env.getAD_Client_ID(method.getCtx()));
params.add(reg.getMFA_Registration_ID());
StringBuilder sql = new StringBuilder();
sql.append("UPDATE MFA_Registration"
+ " SET IsActive='N'"
+ " WHERE AD_User_ID=?"
+ " AND MFA_Method_ID=?"
+ " AND AD_Client_ID=?"
+ " AND IsValid='N'"
+ " AND IsActive='Y'"
+ " AND MFA_Registration_ID!=?");
if (prm != null) {
sql.append(" AND ParameterValue=?");
params.add(prm);
}
DB.executeUpdateEx(sql.toString(), params.toArray(), method.get_TrxName());
}
/**
* Set record as preferred, and set all the others from this user as not preferred
*/
@Override
public void setIsUserMFAPreferred(boolean IsUserMFAPreferred) {
super.setIsUserMFAPreferred(IsUserMFAPreferred);
if (IsUserMFAPreferred) {
int userId = getAD_User_ID();
int clientId = getAD_Client_ID();
int regId = getMFA_Registration_ID();
final String sql = ""
+ "UPDATE MFA_Registration"
+ " SET IsUserMFAPreferred='N'"
+ " WHERE AD_User_ID=?"
+ " AND AD_Client_ID=?"
+ " AND IsUserMFAPreferred='Y'"
+ " AND MFA_Registration_ID!=?";
DB.executeUpdateEx(sql, new Object[] {userId, clientId, regId}, get_TrxName());
}
}
/**
* Get the valid registrations from this user
* @return
*/
public static List<MMFARegistration> getValidRegistrationsFromUser() {
final String where = "IsValid ='Y' AND AD_User_ID=? AND AD_Client_ID IN (0,?)";
List<MMFARegistration> ret = new Query(Env.getCtx(), Table_Name, where, null)
.setParameters(Env.getAD_User_ID(Env.getCtx()), Env.getAD_Client_ID(Env.getCtx()))
.setOnlyActiveRecords(true)
.setOrderBy("IsUserMFAPreferred DESC, Name")
.list();
return ret;
}
/**
* If the user has valid registration mechanisms
* @return
*/
public static boolean userHasValidRegistration() {
final String sql = ""
+ "SELECT COUNT(*)"
+ " FROM MFA_Registration"
+ " WHERE IsActive='Y'"
+ " AND IsValid ='Y'"
+ " AND AD_User_ID=?"
+ " AND AD_Client_ID IN (0,?)";
int cnt = DB.getSQLValueEx(null, sql, Env.getAD_User_ID(Env.getCtx()), Env.getAD_Client_ID(Env.getCtx()));
return cnt > 0;
}
/**
* Generate a validation code using the registered method
* @param reg
* @return
*/
public String generateValidationCode(MMFARegistration reg) {
MMFAMethod method = new MMFAMethod(getCtx(), getMFA_Method_ID(), get_TrxName());
IMFAMechanism mechanism = method.getMFAMechanism();
String msg = mechanism.generateValidationCode(reg);
return msg;
}
/**
* Validate the code using the registered method
* @param reg
* @param code
* @param setPreferred
* @return
*/
public String validateCode(MMFARegistration reg, String code, boolean setPreferred) {
MMFAMethod method = new MMFAMethod(getCtx(), getMFA_Method_ID(), get_TrxName());
IMFAMechanism mechanism = method.getMFAMechanism();
String msg = mechanism.validateCode(reg, code, setPreferred);
return msg;
}
/**
* Set User/Contact.
* @param AD_User_ID
* User within the system - Internal or Business Partner Contact
* Overridden to allow saving System record (zero ID)
*/
@Override
public void setAD_User_ID (int AD_User_ID)
{
if (AD_User_ID == 0)
set_ValueNoCheck (COLUMNNAME_AD_User_ID, AD_User_ID);
else
super.setAD_User_ID(AD_User_ID);
} //setAD_User_ID
} // MMFARegistration

View File

@ -43,7 +43,7 @@ public class MSysConfig extends X_AD_SysConfig
/**
*
*/
private static final long serialVersionUID = 8581992138870649241L;
private static final long serialVersionUID = 2454757193097243912L;
public static final String ADDRESS_VALIDATION = "ADDRESS_VALIDATION";
public static final String ALERT_SEND_ATTACHMENT_AS_XLS = "ALERT_SEND_ATTACHMENT_AS_XLS";
@ -124,6 +124,8 @@ public class MSysConfig extends X_AD_SysConfig
public static final String MAX_RESULTS_PER_SEARCH_IN_DOCUMENT_CONTROLLER = "MAX_RESULTS_PER_SEARCH_IN_DOCUMENT_CONTROLLER";
public static final String MAX_TEXT_LENGTH_ON_GRID_VIEW = "MAX_TEXT_LENGTH_ON_GRID_VIEW";
public static final String MENU_INFOUPDATER_SLEEP_MS = "MENU_INFOUPDATER_SLEEP_MS";
public static final String MFA_NTP_TIMEOUT_IN_MILLISECONDS = "MFA_NTP_TIMEOUT_IN_MILLISECONDS";
public static final String MFA_REGISTERED_DEVICE_EXPIRATION_DAYS = "MFA_REGISTERED_DEVICE_EXPIRATION_DAYS";
public static final String MONITOR_INITIAL_WAIT_FOR_CLUSTER_IN_SECONDS = "MONITOR_INITIAL_WAIT_FOR_CLUSTER_IN_SECONDS";
public static final String MONITOR_MAX_WAIT_FOR_CLUSTER_IN_SECONDS = "MONITOR_MAX_WAIT_FOR_CLUSTER_IN_SECONDS";
public static final String MFG_ValidateCostsDifferenceOnCreate = "MFG_ValidateCostsDifferenceOnCreate";

View File

@ -60,6 +60,7 @@ public class SystemIDs
public final static int FORM_REPORT_WIZARD = 200002;
public final static int FORM_SETUP_WIZARD = 200000;
public final static int FORM_ADD_AUTHORIZATION = 200016;
public final static int FORM_MFA_REGISTER = 200017;
public final static int MENU_NOTICE = 233;

View File

@ -0,0 +1,355 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2012 ComPiere, Inc. All Rights Reserved. *
* This program is free software, you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program, if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
/** Generated Model - DO NOT CHANGE */
package org.compiere.model;
import java.sql.ResultSet;
import java.util.Properties;
import org.compiere.util.KeyNamePair;
/** Generated Model for MFA_Method
* @author iDempiere (generated)
* @version Release 8.2 - $Id$ */
public class X_MFA_Method extends PO implements I_MFA_Method, I_Persistent
{
/**
*
*/
private static final long serialVersionUID = 20210605L;
/** Standard Constructor */
public X_MFA_Method (Properties ctx, int MFA_Method_ID, String trxName)
{
super (ctx, MFA_Method_ID, trxName);
/** if (MFA_Method_ID == 0)
{
setMethod (null);
setMFA_Method_ID (0);
setName (null);
} */
}
/** Load Constructor */
public X_MFA_Method (Properties ctx, ResultSet rs, String trxName)
{
super (ctx, rs, trxName);
}
/** AccessLevel
* @return 4 - System
*/
protected int get_AccessLevel()
{
return accessLevel.intValue();
}
/** Load Meta Data */
protected POInfo initPO (Properties ctx)
{
POInfo poi = POInfo.getPOInfo (ctx, Table_ID, get_TrxName());
return poi;
}
public String toString()
{
StringBuilder sb = new StringBuilder ("X_MFA_Method[")
.append(get_ID()).append(",Name=").append(getName()).append("]");
return sb.toString();
}
/** Set Description.
@param Description
Optional short description of the record
*/
public void setDescription (String Description)
{
set_Value (COLUMNNAME_Description, Description);
}
/** Get Description.
@return Optional short description of the record
*/
public String getDescription ()
{
return (String)get_Value(COLUMNNAME_Description);
}
/** Set Expire in Minutes.
@param ExpireInMinutes Expire in Minutes */
public void setExpireInMinutes (int ExpireInMinutes)
{
set_Value (COLUMNNAME_ExpireInMinutes, Integer.valueOf(ExpireInMinutes));
}
/** Get Expire in Minutes.
@return Expire in Minutes */
public int getExpireInMinutes ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_ExpireInMinutes);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set Comment/Help.
@param Help
Comment or Hint
*/
public void setHelp (String Help)
{
set_Value (COLUMNNAME_Help, Help);
}
/** Get Comment/Help.
@return Comment or Hint
*/
public String getHelp ()
{
return (String)get_Value(COLUMNNAME_Help);
}
/** Method AD_Reference_ID=200187 */
public static final int METHOD_AD_Reference_ID=200187;
/** Time-Based One-Time Password = TOTP */
public static final String METHOD_Time_BasedOne_TimePassword = "TOTP";
/** EMail = EMail */
public static final String METHOD_EMail = "EMail";
/** Set Method.
@param Method Method */
public void setMethod (String Method)
{
set_Value (COLUMNNAME_Method, Method);
}
/** Get Method.
@return Method */
public String getMethod ()
{
return (String)get_Value(COLUMNNAME_Method);
}
/** Set Allowed Time Period Discrepancy.
@param MFAAllowedTimeDiscrepancy Allowed Time Period Discrepancy */
public void setMFAAllowedTimeDiscrepancy (int MFAAllowedTimeDiscrepancy)
{
set_Value (COLUMNNAME_MFAAllowedTimeDiscrepancy, Integer.valueOf(MFAAllowedTimeDiscrepancy));
}
/** Get Allowed Time Period Discrepancy.
@return Allowed Time Period Discrepancy */
public int getMFAAllowedTimeDiscrepancy ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_MFAAllowedTimeDiscrepancy);
if (ii == null)
return 0;
return ii.intValue();
}
public org.compiere.model.I_AD_Element getMFA_ElementPrm() throws RuntimeException
{
return (org.compiere.model.I_AD_Element)MTable.get(getCtx(), org.compiere.model.I_AD_Element.Table_Name)
.getPO(getMFA_ElementPrm_ID(), get_TrxName()); }
/** Set Parameter Element.
@param MFA_ElementPrm_ID Parameter Element */
public void setMFA_ElementPrm_ID (int MFA_ElementPrm_ID)
{
if (MFA_ElementPrm_ID < 1)
set_Value (COLUMNNAME_MFA_ElementPrm_ID, null);
else
set_Value (COLUMNNAME_MFA_ElementPrm_ID, Integer.valueOf(MFA_ElementPrm_ID));
}
/** Get Parameter Element.
@return Parameter Element */
public int getMFA_ElementPrm_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_MFA_ElementPrm_ID);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set Issuer.
@param MFAIssuer Issuer */
public void setMFAIssuer (String MFAIssuer)
{
set_Value (COLUMNNAME_MFAIssuer, MFAIssuer);
}
/** Get Issuer.
@return Issuer */
public String getMFAIssuer ()
{
return (String)get_Value(COLUMNNAME_MFAIssuer);
}
/** Set MFA Method.
@param MFA_Method_ID
Multi-factor Authentication Method
*/
public void setMFA_Method_ID (int MFA_Method_ID)
{
if (MFA_Method_ID < 1)
set_ValueNoCheck (COLUMNNAME_MFA_Method_ID, null);
else
set_ValueNoCheck (COLUMNNAME_MFA_Method_ID, Integer.valueOf(MFA_Method_ID));
}
/** Get MFA Method.
@return Multi-factor Authentication Method
*/
public int getMFA_Method_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_MFA_Method_ID);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set MFA_Method_UU.
@param MFA_Method_UU MFA_Method_UU */
public void setMFA_Method_UU (String MFA_Method_UU)
{
set_Value (COLUMNNAME_MFA_Method_UU, MFA_Method_UU);
}
/** Get MFA_Method_UU.
@return MFA_Method_UU */
public String getMFA_Method_UU ()
{
return (String)get_Value(COLUMNNAME_MFA_Method_UU);
}
/** MFATimeProvider AD_Reference_ID=200189 */
public static final int MFATIMEPROVIDER_AD_Reference_ID=200189;
/** System = S */
public static final String MFATIMEPROVIDER_System = "S";
/** Ntp = N */
public static final String MFATIMEPROVIDER_Ntp = "N";
/** Set Time Provider.
@param MFATimeProvider Time Provider */
public void setMFATimeProvider (String MFATimeProvider)
{
set_Value (COLUMNNAME_MFATimeProvider, MFATimeProvider);
}
/** Get Time Provider.
@return Time Provider */
public String getMFATimeProvider ()
{
return (String)get_Value(COLUMNNAME_MFATimeProvider);
}
/** Set Time Server.
@param MFATimeServer Time Server */
public void setMFATimeServer (String MFATimeServer)
{
set_Value (COLUMNNAME_MFATimeServer, MFATimeServer);
}
/** Get Time Server.
@return Time Server */
public String getMFATimeServer ()
{
return (String)get_Value(COLUMNNAME_MFATimeServer);
}
/** MFAType AD_Reference_ID=200188 */
public static final int MFATYPE_AD_Reference_ID=200188;
/** Something you Know = K */
public static final String MFATYPE_SomethingYouKnow = "K";
/** Something you Have = H */
public static final String MFATYPE_SomethingYouHave = "H";
/** Something you Are (Biometrics) = A */
public static final String MFATYPE_SomethingYouAreBiometrics = "A";
/** Location = L */
public static final String MFATYPE_Location = "L";
/** Set MFA Type.
@param MFAType
Multi-factor authentication type (Something you Know/Have/Are, Location)
*/
public void setMFAType (String MFAType)
{
set_Value (COLUMNNAME_MFAType, MFAType);
}
/** Get MFA Type.
@return Multi-factor authentication type (Something you Know/Have/Are, Location)
*/
public String getMFAType ()
{
return (String)get_Value(COLUMNNAME_MFAType);
}
/** Set Name.
@param Name
Alphanumeric identifier of the entity
*/
public void setName (String Name)
{
set_Value (COLUMNNAME_Name, Name);
}
/** Get Name.
@return Alphanumeric identifier of the entity
*/
public String getName ()
{
return (String)get_Value(COLUMNNAME_Name);
}
/** Get Record ID/ColumnName
@return ID/ColumnName pair
*/
public KeyNamePair getKeyNamePair()
{
return new KeyNamePair(get_ID(), getName());
}
public org.compiere.model.I_R_MailText getR_MailText() throws RuntimeException
{
return (org.compiere.model.I_R_MailText)MTable.get(getCtx(), org.compiere.model.I_R_MailText.Table_Name)
.getPO(getR_MailText_ID(), get_TrxName()); }
/** Set Mail Template.
@param R_MailText_ID
Text templates for mailings
*/
public void setR_MailText_ID (int R_MailText_ID)
{
if (R_MailText_ID < 1)
set_Value (COLUMNNAME_R_MailText_ID, null);
else
set_Value (COLUMNNAME_R_MailText_ID, Integer.valueOf(R_MailText_ID));
}
/** Get Mail Template.
@return Text templates for mailings
*/
public int getR_MailText_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_R_MailText_ID);
if (ii == null)
return 0;
return ii.intValue();
}
}

View File

@ -0,0 +1,187 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2012 ComPiere, Inc. All Rights Reserved. *
* This program is free software, you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program, if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
/** Generated Model - DO NOT CHANGE */
package org.compiere.model;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.Properties;
/** Generated Model for MFA_RegisteredDevice
* @author iDempiere (generated)
* @version Release 8.2 - $Id$ */
public class X_MFA_RegisteredDevice extends PO implements I_MFA_RegisteredDevice, I_Persistent
{
/**
*
*/
private static final long serialVersionUID = 20210605L;
/** Standard Constructor */
public X_MFA_RegisteredDevice (Properties ctx, int MFA_RegisteredDevice_ID, String trxName)
{
super (ctx, MFA_RegisteredDevice_ID, trxName);
/** if (MFA_RegisteredDevice_ID == 0)
{
setAD_User_ID (0);
setMFADeviceIdentifier (null);
setMFA_RegisteredDevice_ID (0);
} */
}
/** Load Constructor */
public X_MFA_RegisteredDevice (Properties ctx, ResultSet rs, String trxName)
{
super (ctx, rs, trxName);
}
/** AccessLevel
* @return 6 - System - Client
*/
protected int get_AccessLevel()
{
return accessLevel.intValue();
}
/** Load Meta Data */
protected POInfo initPO (Properties ctx)
{
POInfo poi = POInfo.getPOInfo (ctx, Table_ID, get_TrxName());
return poi;
}
public String toString()
{
StringBuilder sb = new StringBuilder ("X_MFA_RegisteredDevice[")
.append(get_ID()).append("]");
return sb.toString();
}
public org.compiere.model.I_AD_User getAD_User() throws RuntimeException
{
return (org.compiere.model.I_AD_User)MTable.get(getCtx(), org.compiere.model.I_AD_User.Table_Name)
.getPO(getAD_User_ID(), get_TrxName()); }
/** Set User/Contact.
@param AD_User_ID
User within the system - Internal or Business Partner Contact
*/
public void setAD_User_ID (int AD_User_ID)
{
if (AD_User_ID < 1)
set_Value (COLUMNNAME_AD_User_ID, null);
else
set_Value (COLUMNNAME_AD_User_ID, Integer.valueOf(AD_User_ID));
}
/** Get User/Contact.
@return User within the system - Internal or Business Partner Contact
*/
public int getAD_User_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_AD_User_ID);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set Expire On.
@param Expiration
Expire On
*/
public void setExpiration (Timestamp Expiration)
{
set_Value (COLUMNNAME_Expiration, Expiration);
}
/** Get Expire On.
@return Expire On
*/
public Timestamp getExpiration ()
{
return (Timestamp)get_Value(COLUMNNAME_Expiration);
}
/** Set Comment/Help.
@param Help
Comment or Hint
*/
public void setHelp (String Help)
{
set_Value (COLUMNNAME_Help, Help);
}
/** Get Comment/Help.
@return Comment or Hint
*/
public String getHelp ()
{
return (String)get_Value(COLUMNNAME_Help);
}
/** Set MFA Device Identifier.
@param MFADeviceIdentifier
Multi-factor Authentication Device Identifier
*/
public void setMFADeviceIdentifier (String MFADeviceIdentifier)
{
set_Value (COLUMNNAME_MFADeviceIdentifier, MFADeviceIdentifier);
}
/** Get MFA Device Identifier.
@return Multi-factor Authentication Device Identifier
*/
public String getMFADeviceIdentifier ()
{
return (String)get_Value(COLUMNNAME_MFADeviceIdentifier);
}
/** Set MFA Registered Device.
@param MFA_RegisteredDevice_ID MFA Registered Device */
public void setMFA_RegisteredDevice_ID (int MFA_RegisteredDevice_ID)
{
if (MFA_RegisteredDevice_ID < 1)
set_ValueNoCheck (COLUMNNAME_MFA_RegisteredDevice_ID, null);
else
set_ValueNoCheck (COLUMNNAME_MFA_RegisteredDevice_ID, Integer.valueOf(MFA_RegisteredDevice_ID));
}
/** Get MFA Registered Device.
@return MFA Registered Device */
public int getMFA_RegisteredDevice_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_MFA_RegisteredDevice_ID);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set MFA_RegisteredDevice_UU.
@param MFA_RegisteredDevice_UU MFA_RegisteredDevice_UU */
public void setMFA_RegisteredDevice_UU (String MFA_RegisteredDevice_UU)
{
set_Value (COLUMNNAME_MFA_RegisteredDevice_UU, MFA_RegisteredDevice_UU);
}
/** Get MFA_RegisteredDevice_UU.
@return MFA_RegisteredDevice_UU */
public String getMFA_RegisteredDevice_UU ()
{
return (String)get_Value(COLUMNNAME_MFA_RegisteredDevice_UU);
}
}

View File

@ -0,0 +1,391 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2012 ComPiere, Inc. All Rights Reserved. *
* This program is free software, you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program, if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
/** Generated Model - DO NOT CHANGE */
package org.compiere.model;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.Properties;
import org.compiere.util.KeyNamePair;
/** Generated Model for MFA_Registration
* @author iDempiere (generated)
* @version Release 8.2 - $Id$ */
public class X_MFA_Registration extends PO implements I_MFA_Registration, I_Persistent
{
/**
*
*/
private static final long serialVersionUID = 20210607L;
/** Standard Constructor */
public X_MFA_Registration (Properties ctx, int MFA_Registration_ID, String trxName)
{
super (ctx, MFA_Registration_ID, trxName);
/** if (MFA_Registration_ID == 0)
{
setAD_User_ID (0);
setIsUserMFAPreferred (false);
// N
setIsValid (false);
// N
setMFA_Method_ID (0);
setMFA_Registration_ID (0);
} */
}
/** Load Constructor */
public X_MFA_Registration (Properties ctx, ResultSet rs, String trxName)
{
super (ctx, rs, trxName);
}
/** AccessLevel
* @return 6 - System - Client
*/
protected int get_AccessLevel()
{
return accessLevel.intValue();
}
/** Load Meta Data */
protected POInfo initPO (Properties ctx)
{
POInfo poi = POInfo.getPOInfo (ctx, Table_ID, get_TrxName());
return poi;
}
public String toString()
{
StringBuilder sb = new StringBuilder ("X_MFA_Registration[")
.append(get_ID()).append(",Name=").append(getName()).append("]");
return sb.toString();
}
public org.compiere.model.I_AD_User getAD_User() throws RuntimeException
{
return (org.compiere.model.I_AD_User)MTable.get(getCtx(), org.compiere.model.I_AD_User.Table_Name)
.getPO(getAD_User_ID(), get_TrxName()); }
/** Set User/Contact.
@param AD_User_ID
User within the system - Internal or Business Partner Contact
*/
public void setAD_User_ID (int AD_User_ID)
{
if (AD_User_ID < 1)
set_Value (COLUMNNAME_AD_User_ID, null);
else
set_Value (COLUMNNAME_AD_User_ID, Integer.valueOf(AD_User_ID));
}
/** Get User/Contact.
@return User within the system - Internal or Business Partner Contact
*/
public int getAD_User_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_AD_User_ID);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set Expire On.
@param Expiration
Expire On
*/
public void setExpiration (Timestamp Expiration)
{
set_Value (COLUMNNAME_Expiration, Expiration);
}
/** Get Expire On.
@return Expire On
*/
public Timestamp getExpiration ()
{
return (Timestamp)get_Value(COLUMNNAME_Expiration);
}
/** Set Failed Login Count.
@param FailedLoginCount Failed Login Count */
public void setFailedLoginCount (int FailedLoginCount)
{
set_ValueNoCheck (COLUMNNAME_FailedLoginCount, Integer.valueOf(FailedLoginCount));
}
/** Get Failed Login Count.
@return Failed Login Count */
public int getFailedLoginCount ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_FailedLoginCount);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set Comment/Help.
@param Help
Comment or Hint
*/
public void setHelp (String Help)
{
set_Value (COLUMNNAME_Help, Help);
}
/** Get Comment/Help.
@return Comment or Hint
*/
public String getHelp ()
{
return (String)get_Value(COLUMNNAME_Help);
}
/** Set Preferred.
@param IsUserMFAPreferred Preferred */
public void setIsUserMFAPreferred (boolean IsUserMFAPreferred)
{
set_Value (COLUMNNAME_IsUserMFAPreferred, Boolean.valueOf(IsUserMFAPreferred));
}
/** Get Preferred.
@return Preferred */
public boolean isUserMFAPreferred ()
{
Object oo = get_Value(COLUMNNAME_IsUserMFAPreferred);
if (oo != null)
{
if (oo instanceof Boolean)
return ((Boolean)oo).booleanValue();
return "Y".equals(oo);
}
return false;
}
/** Set Valid.
@param IsValid
Element is valid
*/
public void setIsValid (boolean IsValid)
{
set_Value (COLUMNNAME_IsValid, Boolean.valueOf(IsValid));
}
/** Get Valid.
@return Element is valid
*/
public boolean isValid ()
{
Object oo = get_Value(COLUMNNAME_IsValid);
if (oo != null)
{
if (oo instanceof Boolean)
return ((Boolean)oo).booleanValue();
return "Y".equals(oo);
}
return false;
}
/** Set Last Failure.
@param LastFailure Last Failure */
public void setLastFailure (Timestamp LastFailure)
{
set_Value (COLUMNNAME_LastFailure, LastFailure);
}
/** Get Last Failure.
@return Last Failure */
public Timestamp getLastFailure ()
{
return (Timestamp)get_Value(COLUMNNAME_LastFailure);
}
/** Set Last Success.
@param LastSuccess Last Success */
public void setLastSuccess (Timestamp LastSuccess)
{
set_Value (COLUMNNAME_LastSuccess, LastSuccess);
}
/** Get Last Success.
@return Last Success */
public Timestamp getLastSuccess ()
{
return (Timestamp)get_Value(COLUMNNAME_LastSuccess);
}
/** Set Last MFA Secret.
@param MFALastSecret Last MFA Secret */
public void setMFALastSecret (String MFALastSecret)
{
set_Value (COLUMNNAME_MFALastSecret, MFALastSecret);
}
/** Get Last MFA Secret.
@return Last MFA Secret */
public String getMFALastSecret ()
{
return (String)get_Value(COLUMNNAME_MFALastSecret);
}
public org.compiere.model.I_MFA_Method getMFA_Method() throws RuntimeException
{
return (org.compiere.model.I_MFA_Method)MTable.get(getCtx(), org.compiere.model.I_MFA_Method.Table_Name)
.getPO(getMFA_Method_ID(), get_TrxName()); }
/** Set MFA Method.
@param MFA_Method_ID
Multi-factor Authentication Method
*/
public void setMFA_Method_ID (int MFA_Method_ID)
{
if (MFA_Method_ID < 1)
set_Value (COLUMNNAME_MFA_Method_ID, null);
else
set_Value (COLUMNNAME_MFA_Method_ID, Integer.valueOf(MFA_Method_ID));
}
/** Get MFA Method.
@return Multi-factor Authentication Method
*/
public int getMFA_Method_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_MFA_Method_ID);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set MFA Registration.
@param MFA_Registration_ID MFA Registration */
public void setMFA_Registration_ID (int MFA_Registration_ID)
{
if (MFA_Registration_ID < 1)
set_ValueNoCheck (COLUMNNAME_MFA_Registration_ID, null);
else
set_ValueNoCheck (COLUMNNAME_MFA_Registration_ID, Integer.valueOf(MFA_Registration_ID));
}
/** Get MFA Registration.
@return MFA Registration */
public int getMFA_Registration_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_MFA_Registration_ID);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set MFA_Registration_UU.
@param MFA_Registration_UU MFA_Registration_UU */
public void setMFA_Registration_UU (String MFA_Registration_UU)
{
set_Value (COLUMNNAME_MFA_Registration_UU, MFA_Registration_UU);
}
/** Get MFA_Registration_UU.
@return MFA_Registration_UU */
public String getMFA_Registration_UU ()
{
return (String)get_Value(COLUMNNAME_MFA_Registration_UU);
}
/** Set MFA Secret.
@param MFASecret
Multi-factor Authentication Secret
*/
public void setMFASecret (String MFASecret)
{
set_Value (COLUMNNAME_MFASecret, MFASecret);
}
/** Get MFA Secret.
@return Multi-factor Authentication Secret
*/
public String getMFASecret ()
{
return (String)get_Value(COLUMNNAME_MFASecret);
}
/** Set Unregistered at.
@param MFAUnregisteredAt Unregistered at */
public void setMFAUnregisteredAt (Timestamp MFAUnregisteredAt)
{
set_Value (COLUMNNAME_MFAUnregisteredAt, MFAUnregisteredAt);
}
/** Get Unregistered at.
@return Unregistered at */
public Timestamp getMFAUnregisteredAt ()
{
return (Timestamp)get_Value(COLUMNNAME_MFAUnregisteredAt);
}
/** Set Validated at.
@param MFAValidatedAt Validated at */
public void setMFAValidatedAt (Timestamp MFAValidatedAt)
{
set_Value (COLUMNNAME_MFAValidatedAt, MFAValidatedAt);
}
/** Get Validated at.
@return Validated at */
public Timestamp getMFAValidatedAt ()
{
return (Timestamp)get_Value(COLUMNNAME_MFAValidatedAt);
}
/** Set Name.
@param Name
Alphanumeric identifier of the entity
*/
public void setName (String Name)
{
set_Value (COLUMNNAME_Name, Name);
}
/** Get Name.
@return Alphanumeric identifier of the entity
*/
public String getName ()
{
return (String)get_Value(COLUMNNAME_Name);
}
/** Get Record ID/ColumnName
@return ID/ColumnName pair
*/
public KeyNamePair getKeyNamePair()
{
return new KeyNamePair(get_ID(), getName());
}
/** Set Parameter Value.
@param ParameterValue Parameter Value */
public void setParameterValue (String ParameterValue)
{
set_Value (COLUMNNAME_ParameterValue, ParameterValue);
}
/** Get Parameter Value.
@return Parameter Value */
public String getParameterValue ()
{
return (String)get_Value(COLUMNNAME_ParameterValue);
}
}

View File

@ -0,0 +1,154 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2012 ComPiere, Inc. All Rights Reserved. *
* This program is free software, you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program, if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
/** Generated Model - DO NOT CHANGE */
package org.compiere.model;
import java.sql.ResultSet;
import java.util.Properties;
/** Generated Model for MFA_Rule
* @author iDempiere (generated)
* @version Release 8.2 - $Id$ */
public class X_MFA_Rule extends PO implements I_MFA_Rule, I_Persistent
{
/**
*
*/
private static final long serialVersionUID = 20210605L;
/** Standard Constructor */
public X_MFA_Rule (Properties ctx, int MFA_Rule_ID, String trxName)
{
super (ctx, MFA_Rule_ID, trxName);
/** if (MFA_Rule_ID == 0)
{
setMFA_Method_ID (0);
setMFA_Rule_ID (0);
} */
}
/** Load Constructor */
public X_MFA_Rule (Properties ctx, ResultSet rs, String trxName)
{
super (ctx, rs, trxName);
}
/** AccessLevel
* @return 6 - System - Client
*/
protected int get_AccessLevel()
{
return accessLevel.intValue();
}
/** Load Meta Data */
protected POInfo initPO (Properties ctx)
{
POInfo poi = POInfo.getPOInfo (ctx, Table_ID, get_TrxName());
return poi;
}
public String toString()
{
StringBuilder sb = new StringBuilder ("X_MFA_Rule[")
.append(get_ID()).append("]");
return sb.toString();
}
/** Set Comment/Help.
@param Help
Comment or Hint
*/
public void setHelp (String Help)
{
set_Value (COLUMNNAME_Help, Help);
}
/** Get Comment/Help.
@return Comment or Hint
*/
public String getHelp ()
{
return (String)get_Value(COLUMNNAME_Help);
}
public org.compiere.model.I_MFA_Method getMFA_Method() throws RuntimeException
{
return (org.compiere.model.I_MFA_Method)MTable.get(getCtx(), org.compiere.model.I_MFA_Method.Table_Name)
.getPO(getMFA_Method_ID(), get_TrxName()); }
/** Set MFA Method.
@param MFA_Method_ID
Multi-factor Authentication Method
*/
public void setMFA_Method_ID (int MFA_Method_ID)
{
if (MFA_Method_ID < 1)
set_ValueNoCheck (COLUMNNAME_MFA_Method_ID, null);
else
set_ValueNoCheck (COLUMNNAME_MFA_Method_ID, Integer.valueOf(MFA_Method_ID));
}
/** Get MFA Method.
@return Multi-factor Authentication Method
*/
public int getMFA_Method_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_MFA_Method_ID);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set MFA Rule.
@param MFA_Rule_ID
Multi-factor Authentication Rule
*/
public void setMFA_Rule_ID (int MFA_Rule_ID)
{
if (MFA_Rule_ID < 1)
set_ValueNoCheck (COLUMNNAME_MFA_Rule_ID, null);
else
set_ValueNoCheck (COLUMNNAME_MFA_Rule_ID, Integer.valueOf(MFA_Rule_ID));
}
/** Get MFA Rule.
@return Multi-factor Authentication Rule
*/
public int getMFA_Rule_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_MFA_Rule_ID);
if (ii == null)
return 0;
return ii.intValue();
}
/** Set MFA_Rule_UU.
@param MFA_Rule_UU MFA_Rule_UU */
public void setMFA_Rule_UU (String MFA_Rule_UU)
{
set_Value (COLUMNNAME_MFA_Rule_UU, MFA_Rule_UU);
}
/** Get MFA_Rule_UU.
@return MFA_Rule_UU */
public String getMFA_Rule_UU ()
{
return (String)get_Value(COLUMNNAME_MFA_Rule_UU);
}
}

View File

@ -0,0 +1,103 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Sponsor: *
* - FH *
* Contributors: *
* - Carlos Ruiz *
**********************************************************************/
package org.compiere.process;
import java.sql.Timestamp;
import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException;
import org.compiere.model.IMFAMechanism;
import org.compiere.model.MMFAMethod;
import org.compiere.model.MMFARegistration;
import org.compiere.util.Msg;
import org.compiere.util.Util;
/**
* IDEMPIERE-4782
* @author Carlos Ruiz - globalqss - BX Service
*/
public class MFACompleteRegistration extends SvrProcess {
/* MFA Registration */
private int p_MFA_Registration_ID = 0;
/* Validation Code */
private String p_MFAValidationCode = null;
/* Name */
private String p_Name = null;
/* Preferred */
private boolean p_IsUserMFAPreferred = false;
@Override
protected void prepare() {
for (ProcessInfoParameter para : getParameter()) {
String name = para.getParameterName();
switch (name) {
case "MFA_Registration_ID": p_MFA_Registration_ID = para.getParameterAsInt(); break;
case "MFAValidationCode": p_MFAValidationCode = para.getParameterAsString(); break;
case "Name": p_Name = para.getParameterAsString(); break;
case "IsUserMFAPreferred": p_IsUserMFAPreferred = para.getParameterAsBoolean(); break;
default:
if (log.isLoggable(Level.INFO))
log.log(Level.INFO, "Custom Parameter: " + name + "=" + para.getInfo());
break;
}
}
}
/**
* Perform process.
* @return Message
* @throws Exception
*/
protected String doIt() throws Exception {
if (log.isLoggable(Level.INFO))
log.info("MFA_Registration_ID=" + p_MFA_Registration_ID
+ ", MFAValidationCode=" + p_MFAValidationCode
+ ", Name=" + p_Name
+ ", IsUserMFAPreferred=" + p_IsUserMFAPreferred);
MMFARegistration reg = new MMFARegistration(getCtx(), p_MFA_Registration_ID, get_TrxName());
if (reg.isValid())
throw new AdempiereException(Msg.getMsg(getCtx(), "MFARegistrationAlreadyValid"));
if (Util.isEmpty(p_MFAValidationCode))
throw new AdempiereException(Msg.getMsg(getCtx(), "MFACodeRequired"));
Timestamp now = new Timestamp(System.currentTimeMillis());
if (reg.getExpiration() != null && now.after(reg.getExpiration())) {
reg.setIsActive(false);
reg.saveEx(null);
throw new AdempiereException(Msg.getMsg(getCtx(), "MFARegistrationExpired"));
}
MMFAMethod method = new MMFAMethod(getCtx(), reg.getMFA_Method_ID(), get_TrxName());
IMFAMechanism mechanism = method.getMFAMechanism();
String msg = mechanism.complete(getCtx(), reg, p_MFAValidationCode, p_Name, p_IsUserMFAPreferred, get_TrxName());
return msg;
}
} // RegisterMFA

View File

@ -0,0 +1,103 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Sponsor: *
* - FH *
* Contributors: *
* - Carlos Ruiz *
**********************************************************************/
package org.compiere.process;
import java.util.logging.Level;
import org.compiere.model.IMFAMechanism;
import org.compiere.model.MMFAMethod;
import org.compiere.model.MMFARegistration;
import org.compiere.util.AdempiereSystemError;
import org.compiere.util.Msg;
/**
* IDEMPIERE-4782
* @author Carlos Ruiz - globalqss - BX Service
*/
public class MFARegister extends SvrProcess {
/* MFA Method */
private int p_MFA_Method_ID = 0;
/* Parameter Value */
private String p_ParameterValue = null;
/* Return array from mechanism */
protected Object[] retArray;
@Override
protected void prepare() {
for (ProcessInfoParameter para : getParameter()) {
String name = para.getParameterName();
switch (name) {
case "MFA_Method_ID": p_MFA_Method_ID = para.getParameterAsInt(); break;
case "ParameterValue": p_ParameterValue = para.getParameterAsString(); break;
default:
if (log.isLoggable(Level.INFO))
log.log(Level.INFO, "Custom Parameter: " + name + "=" + para.getInfo());
break;
}
}
}
/**
* Perform process.
* @return Message
* @throws Exception
*/
protected String doIt() throws Exception {
if (log.isLoggable(Level.INFO))
log.info("MFA_Method_ID=" + p_MFA_Method_ID
+ ", ParameterValue=" + p_ParameterValue);
MMFAMethod method = new MMFAMethod(getCtx(), p_MFA_Method_ID, get_TrxName());
IMFAMechanism mechanism = method.getMFAMechanism();
retArray = mechanism.register(getCtx(), method, p_ParameterValue, get_TrxName());
if (retArray == null || retArray.length == 0 || ! (retArray[0] instanceof String) )
throw new AdempiereSystemError("Wrong return from mechanism.validate");
if (processUI == null) {
for (int i = 0; i < retArray.length; i++) {
if (retArray[i] instanceof String) {
String reti = (String) retArray[i];
if (reti.startsWith("data:image/png;base64,"))
addLog("<img src=\"" + reti + "\" width=\"180\" height=\"180\"/>"); // show QR code
else
addLog((String) reti);
} else if (retArray[i] instanceof MMFARegistration) {
MMFARegistration reg = (MMFARegistration) retArray[i];
addLog(0, null, null, Msg.parseTranslation(getCtx(), "@Created@: @MFA_Registration_ID@"), MMFARegistration.Table_ID, reg.getMFA_Registration_ID());
}
}
}
// else -> when the process is driven by zkwebui it will show the MFARegisterForm next, no need to save the parameters
return "@OK@";
}
} // MFARegister

View File

@ -0,0 +1,96 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Sponsor: *
* - FH *
* Contributors: *
* - Carlos Ruiz *
**********************************************************************/
package org.compiere.process;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.compiere.model.MMFARegisteredDevice;
import org.compiere.model.Query;
import org.compiere.util.Env;
/**
* IDEMPIERE-4782
* @author Carlos Ruiz - globalqss - BX Service
*/
public class MFARevokeDevice extends SvrProcess {
/* Revoke All */
private Boolean p_MFARevokeAll = null;
/* MFA Registered Device */
private int p_MFA_RegisteredDevice_ID = 0;
@Override
protected void prepare() {
for (ProcessInfoParameter para : getParameter()) {
String name = para.getParameterName();
switch (name) {
case "MFARevokeAll": p_MFARevokeAll = para.getParameterAsBoolean(); break;
case "MFA_RegisteredDevice_ID": p_MFA_RegisteredDevice_ID = para.getParameterAsInt(); break;
default:
if (log.isLoggable(Level.INFO))
log.log(Level.INFO, "Custom Parameter: " + name + "=" + para.getInfo());
break;
}
}
}
/**
* Perform process.
* @return Message
* @throws Exception
*/
protected String doIt() throws Exception {
if (log.isLoggable(Level.INFO))
log.info("MFARevokeAll=" + p_MFARevokeAll
+ ", MFA_RegisteredDevice_ID=" + p_MFA_RegisteredDevice_ID);
String where;
List<Object> params = new ArrayList<Object>();
params.add(Env.getAD_User_ID(getCtx()));
if (p_MFARevokeAll) {
where = "AD_User_ID=?";
} else {
where = "AD_User_ID=? AND (MFA_RegisteredDevice_ID=? OR Expiration<=SYSDATE)";
params.add(p_MFA_RegisteredDevice_ID);
}
List<MMFARegisteredDevice> rds = new Query(getCtx(), MMFARegisteredDevice.Table_Name, where, get_TrxName())
.setOnlyActiveRecords(true)
.setClient_ID()
.setParameters(params)
.list();
for (MMFARegisteredDevice rd : rds) {
rd.setIsActive(false);
rd.saveEx();
}
return "@OK@";
}
} // MFARevokeDevice

View File

@ -0,0 +1,75 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Sponsor: *
* - FH *
* Contributors: *
* - Carlos Ruiz *
**********************************************************************/
package org.compiere.process;
import java.sql.Timestamp;
import java.util.logging.Level;
import org.compiere.model.MMFARegistration;
/**
* IDEMPIERE-4782
* @author Carlos Ruiz - globalqss - BX Service
*/
public class MFAUnregister extends SvrProcess {
/* MFA Registration */
private int p_MFA_Registration_ID = 0;
@Override
protected void prepare() {
for (ProcessInfoParameter para : getParameter()) {
String name = para.getParameterName();
switch (name) {
case "MFA_Registration_ID": p_MFA_Registration_ID = para.getParameterAsInt(); break;
default:
if (log.isLoggable(Level.INFO))
log.log(Level.INFO, "Custom Parameter: " + name + "=" + para.getInfo());
break;
}
}
}
/**
* Perform process.
* @return Message
* @throws Exception
*/
protected String doIt() throws Exception {
if (log.isLoggable(Level.INFO))
log.info("MFA_Registration_ID=" + p_MFA_Registration_ID);
MMFARegistration reg = new MMFARegistration(getCtx(), p_MFA_Registration_ID, get_TrxName());
reg.setIsActive(false);
reg.setMFAUnregisteredAt(new Timestamp(System.currentTimeMillis()));
reg.saveEx();
return "@OK@";
}
} // MFAUnregister

View File

@ -38,6 +38,8 @@ import org.compiere.model.I_M_Warehouse;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MClientInfo;
import org.compiere.model.MCountry;
import org.compiere.model.MMFARegisteredDevice;
import org.compiere.model.MMFARegistration;
import org.compiere.model.MRole;
import org.compiere.model.MSysConfig;
import org.compiere.model.MSystem;
@ -1667,4 +1669,17 @@ public class Login
return retValue;
}
/**
* Validate if MFA is required taking into account the registerCookie and the IPAddress
* @param registerCookie
* @return
*/
public boolean isMFARequired(String registerCookie) {
if (registerCookie != null && MMFARegisteredDevice.isValid(registerCookie))
return false;
if (MMFARegistration.userHasValidRegistration())
return true;
return false;
}
} // Login

View File

@ -0,0 +1,290 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - Carlos Ruiz (sponsored by FH) *
**********************************************************************/
package org.idempiere.mfa;
import java.sql.Timestamp;
import java.util.Properties;
import java.util.Random;
import org.adempiere.exceptions.AdempiereException;
import org.compiere.model.IMFAMechanism;
import org.compiere.model.MClient;
import org.compiere.model.MMFAMethod;
import org.compiere.model.MMFARegistration;
import org.compiere.model.MMailText;
import org.compiere.model.MUser;
import org.compiere.model.PO;
import org.compiere.util.EMail;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
public class EMailMechanism implements IMFAMechanism {
/**
* Implement the registration mechanism for EMail Generate random code and
* return in the array
*
* @param ctx
* @param method
* @param prm email
* @param trxName
* @return Object[] - first object is the String with the instructions to follow
* second object is the registration generated
*/
@Override
public Object[] register(Properties ctx, MMFAMethod method, String prm, String trxName) {
if (Util.isEmpty(prm))
throw new AdempiereException(Msg.getMsg(ctx, "MFAEMailRequired"));
if (!EMail.validate(prm))
throw new AdempiereException(Msg.getMsg(ctx, "MFAInvalidEMail"));
if (method.getR_MailText_ID() <= 0)
throw new AdempiereException("EMail method wrongly configured - requires mail template");
// just one time allowed per EMail
if (MMFARegistration.alreadyExistsValid(method, prm))
throw new AdempiereException(Msg.getMsg(ctx, "MFAMethodAlreadyRegistered"));
// Generate a random code and save it in MFA_Registration
String otp = generateRandomString(6);
int expireMinutes = method.getExpireInMinutes();
if (expireMinutes <= 0)
expireMinutes = 15; // default to 15 minutes
MUser user = MUser.get(ctx);
MMFARegistration reg = new MMFARegistration(ctx, 0, trxName);
reg.setName(obfuscateEMail(prm));
reg.setParameterValue(prm);
reg.setMFA_Method_ID(method.getMFA_Method_ID());
reg.setAD_User_ID(user.getAD_User_ID());
reg.setMFASecret(otp);
reg.setIsValid(false);
reg.setIsUserMFAPreferred(false);
reg.setExpiration(new Timestamp(System.currentTimeMillis() + (expireMinutes * 60000)));
reg.saveEx();
// send the email
MClient client = MClient.get(ctx);
MMailText mt = new MMailText(ctx, method.getR_MailText_ID(), trxName);
mt.setLanguage(Env.getContext(ctx, "#AD_Language"));
mt.setUser(user.getAD_User_ID());
mt.setPO(reg);
String message = mt.getMailText(true);
EMail email = client.createEMail(prm, mt.getMailHeader(), message, mt.isHtml());
if (mt.isHtml())
email.setMessageHTML(mt.getMailHeader(), message);
else {
email.setSubject(mt.getMailHeader());
email.setMessageText(message);
}
if (!email.isValid() && !email.isValid(true))
throw new AdempiereException("The EMail is not valid, check configuration");
if (!EMail.SENT_OK.equals(email.send()))
throw new AdempiereException(Msg.getMsg(ctx, "MFAProblemSendingEMail"));
// Invalidate any other previous pending registration with same method and email
MMFARegistration.invalidatePreviousPending(method, prm, reg);
// Notify the user to check the email for the code
Object[] ret = new Object[2];
ret[0] = Msg.getMsg(Env.getCtx(), "MFAEMailCodeSent");
ret[1] = reg;
return ret;
}
/**
* Generates a numeric random string of the specified length
*
* @param len
* @return random String
*/
public static String generateRandomString(int len) {
// String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // if upper alphanumeric is wanted
String chars = "0123456789";
Random rnd = new Random();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++)
sb.append(chars.charAt(rnd.nextInt(chars.length())));
return sb.toString();
}
/**
* Complete/Validate a previous EMail registration
*
* @param ctx
* @param reg The registration object
* @param p_MFAValidationCode The code to be validated
* @param p_Name Optional - a name to assign the registration
* @param get_TrxName
* @return msg A message indicating success, errors throw exception
*/
@Override
public String complete(Properties ctx, MMFARegistration reg, String code, String name, boolean preferred, String trxName) {
boolean valid = code.equals(reg.getMFASecret());
if (! valid) {
reg.setLastFailure(new Timestamp(System.currentTimeMillis()));
reg.setFailedLoginCount(reg.getFailedLoginCount() + 1);
try {
PO.setCrossTenantSafe();
reg.saveEx();
} finally {
PO.clearCrossTenantSafe();
}
throw new AdempiereException(Msg.getMsg(ctx, "MFACodeInvalid"));
}
// valid code
reg.setMFALastSecret(code);
reg.setLastSuccess(new Timestamp(System.currentTimeMillis()));
reg.setFailedLoginCount(0);
reg.setIsValid(true);
reg.setMFAValidatedAt(new Timestamp(System.currentTimeMillis()));
reg.setExpiration(null);
if (!Util.isEmpty(name))
reg.setName(name);
if (preferred)
reg.setIsUserMFAPreferred(true);
try {
PO.setCrossTenantSafe();
reg.saveEx();
} finally {
PO.clearCrossTenantSafe();
}
return Msg.getMsg(ctx, "MFARegistrationCompleted");
}
/**
* Send email with validation code
* @param reg
* @return
*/
@Override
public String generateValidationCode(MMFARegistration reg) {
Properties ctx = reg.getCtx();
MMFAMethod method = new MMFAMethod(ctx, reg.getMFA_Method_ID(), reg.get_TrxName());
// Generate a random code and save it in MFA_Registration
String otp = generateRandomString(6);
int expireMinutes = method.getExpireInMinutes();
if (expireMinutes <= 0)
expireMinutes = 15; // default to 15 minutes
MUser user = MUser.get(reg.getCtx());
reg.setMFASecret(otp);
reg.setExpiration(new Timestamp(System.currentTimeMillis() + (expireMinutes * 60000)));
try {
PO.setCrossTenantSafe();
reg.saveEx();
} finally {
PO.clearCrossTenantSafe();
}
String mail_to = reg.getParameterValue();
// send the email
MClient client = MClient.get(ctx);
MMailText mt = new MMailText(ctx, method.getR_MailText_ID(), reg.get_TrxName());
mt.setLanguage(Env.getContext(ctx, "#AD_Language"));
mt.setUser(user.getAD_User_ID());
mt.setPO(reg);
String message = mt.getMailText(true);
EMail email = client.createEMail(mail_to, mt.getMailHeader(), message, mt.isHtml());
if (mt.isHtml())
email.setMessageHTML(mt.getMailHeader(), message);
else {
email.setSubject(mt.getMailHeader());
email.setMessageText(message);
}
if (!email.isValid() && !email.isValid(true))
throw new AdempiereException("The EMail is not valid, check configuration");
if (!EMail.SENT_OK.equals(email.send()))
throw new AdempiereException(Msg.getMsg(ctx, "MFAProblemSendingEMail"));
return Msg.getMsg(Env.getCtx(), "MFAEMailValidationCodeSent", new Object[] { obfuscateEMail(mail_to) });
}
/**
* Replace internal characters in email with *
* @param mail
* @return
*/
private String obfuscateEMail(String mail) {
StringBuilder mailObfuscated = new StringBuilder();
boolean atFound = false;
for (int idx = 0; idx < mail.length(); idx++) {
char chr = mail.charAt(idx);
if (chr == '@') {
atFound = true;
} else if ((!atFound && idx > 1) || (atFound && idx < mail.length() - 4)) {
chr = '*';
}
mailObfuscated.append(chr);
}
return mailObfuscated.toString();
}
/**
* Validate a code
* @param reg
* @param code
* @param setPreferred
* @return message on error, null when OK
*/
@Override
public String validateCode(MMFARegistration reg, String code, boolean setPreferred) {
Properties ctx = reg.getCtx();
Timestamp now = new Timestamp(System.currentTimeMillis());
if (reg.getExpiration() != null && now.after(reg.getExpiration()))
return Msg.getMsg(ctx, "MFARegistrationExpired");
if (code.equals(reg.getMFALastSecret()))
return Msg.getMsg(ctx, "MFACodeAlreadyConsumed");
boolean valid = code.equals(reg.getMFASecret());
if (! valid) {
reg.setLastFailure(new Timestamp(System.currentTimeMillis()));
reg.setFailedLoginCount(reg.getFailedLoginCount() + 1);
reg.saveEx();
return Msg.getMsg(ctx, "MFACodeInvalid");
}
reg.setMFALastSecret(code);
reg.setLastSuccess(new Timestamp(System.currentTimeMillis()));
reg.setFailedLoginCount(0);
if (setPreferred)
reg.setIsUserMFAPreferred(true);
try {
PO.setCrossTenantSafe();
reg.saveEx();
} finally {
PO.clearCrossTenantSafe();
}
return null;
}
}

View File

@ -0,0 +1,255 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - Carlos Ruiz (sponsored by FH) *
**********************************************************************/
package org.idempiere.mfa;
import static dev.samstevens.totp.util.Utils.getDataUriForImage;
import java.net.UnknownHostException;
import java.sql.Timestamp;
import java.util.Properties;
import org.adempiere.exceptions.AdempiereException;
import org.compiere.model.IMFAMechanism;
import org.compiere.model.MMFAMethod;
import org.compiere.model.MMFARegistration;
import org.compiere.model.MSysConfig;
import org.compiere.model.MUser;
import org.compiere.model.PO;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
import dev.samstevens.totp.code.CodeGenerator;
import dev.samstevens.totp.code.DefaultCodeGenerator;
import dev.samstevens.totp.code.DefaultCodeVerifier;
import dev.samstevens.totp.exceptions.QrGenerationException;
import dev.samstevens.totp.qr.QrData;
import dev.samstevens.totp.qr.QrGenerator;
import dev.samstevens.totp.qr.ZxingPngQrGenerator;
import dev.samstevens.totp.secret.DefaultSecretGenerator;
import dev.samstevens.totp.secret.SecretGenerator;
import dev.samstevens.totp.time.NtpTimeProvider;
import dev.samstevens.totp.time.SystemTimeProvider;
import dev.samstevens.totp.time.TimeProvider;
public class TOTPMechanism implements IMFAMechanism {
/**
* Implement the registration mechanism for TOTP
* Generate the secret and the qrcode and return in the array
* @param ctx
* @param method
* @param prm optional - assigned name from the user
* @param trxName
* @return Object[] - first object is the String with the instructions to follow
* second object is the registration generated
* third message qrcode
* fourth qrcode
* fifth message secret
* sixth secret
*/
@Override
public Object[] register(Properties ctx, MMFAMethod method, String prm, String trxName) {
if (Util.isEmpty(method.getMFAIssuer()))
throw new AdempiereException(Msg.getMsg(ctx, "MFATOTPIssuerRequired"));
if (MMFARegistration.alreadyExistsValid(method, prm))
throw new AdempiereException(Msg.getMsg(ctx, "MFAMethodAlreadyRegistered"));
MUser user = MUser.get(ctx);
String label;
boolean email_login = MSysConfig.getBooleanValue(MSysConfig.USE_EMAIL_FOR_LOGIN, false);
if (email_login)
label = user.getEMail();
else
label = user.getName();
SecretGenerator secretGenerator = new DefaultSecretGenerator();
String secret = secretGenerator.generate();
QrData data = new QrData.Builder()
.label(label)
.secret(secret)
.issuer(method.getMFAIssuer())
.build();
QrGenerator generator = new ZxingPngQrGenerator();
byte[] imageData;
try {
imageData = generator.generate(data);
} catch (QrGenerationException e) {
throw new AdempiereException(Msg.getMsg(Env.getCtx(), "MFATOTPErrorGeneratingQR"), e);
}
String mimeType = generator.getImageMimeType();
String dataUri = getDataUriForImage(imageData, mimeType);
int expireMinutes = method.getExpireInMinutes();
MMFARegistration reg = new MMFARegistration(ctx, 0, trxName);
if (! Util.isEmpty(prm)) {
reg.setName(prm);
reg.setParameterValue(prm);
} else {
reg.setName(label);
}
reg.setMFA_Method_ID(method.getMFA_Method_ID());
reg.setAD_User_ID(user.getAD_User_ID());
reg.setMFASecret(secret);
reg.setIsValid(false);
reg.setIsUserMFAPreferred(false);
if (expireMinutes > 0)
reg.setExpiration(new Timestamp(System.currentTimeMillis() + (expireMinutes*60000)));
reg.saveEx();
// Invalidate any other previous pending registration with same method
MMFARegistration.invalidatePreviousPending(method, prm, reg);
// Notify the user that TOTP mechanism was registered and follow next step
Object[] ret = new Object[6];
ret[0] = Msg.getMsg(Env.getCtx(), "MFATOTPRegistered");
ret[1] = reg;
ret[2] = Msg.getMsg(Env.getCtx(), "MFATOTPImage");
ret[3] = dataUri;
ret[4] = Msg.getMsg(Env.getCtx(), "MFATOTPSecret");
ret[5] = secret;
return ret;
}
/**
* Complete/Validate a previous TOTP registration
* @param ctx
* @param reg The registration object
* @param p_MFAValidationCode The code to be validated
* @param p_Name Optional - a name to assign the registration
* @param trxName
* @return msg A message indicating success, errors throw exception
*/
@Override
public String complete(Properties ctx, MMFARegistration reg, String code, String name, boolean preferred, String trxName) {
if (! isValidCode(ctx, reg, code, trxName))
throw new AdempiereException(Msg.getMsg(ctx, "MFACodeInvalid"));
// valid code
reg.setIsValid(true);
reg.setMFAValidatedAt(new Timestamp(System.currentTimeMillis()));
reg.setExpiration(null);
if (!Util.isEmpty(name))
reg.setName(name);
if (preferred)
reg.setIsUserMFAPreferred(true);
reg.saveEx();
return Msg.getMsg(ctx, "MFARegistrationCompleted");
}
/**
* @param reg
* @param code
* @param trxName
* @return
*/
private boolean isValidCode(Properties ctx, MMFARegistration reg, String code, String trxName) {
MMFAMethod method = new MMFAMethod(ctx, reg.getMFA_Method_ID(), trxName);
TimeProvider timeProvider;
if (MMFAMethod.MFATIMEPROVIDER_Ntp.equals(method.getMFATimeProvider())) {
String ntpServer = method.getMFATimeServer();
if (Util.isEmpty(ntpServer))
ntpServer = "pool.ntp.org";
int timeout = MSysConfig.getIntValue(MSysConfig.MFA_NTP_TIMEOUT_IN_MILLISECONDS, 5000);
try {
timeProvider = new NtpTimeProvider(ntpServer, timeout);
} catch (UnknownHostException e) {
throw new AdempiereException(Msg.getMsg(ctx, "MFANTPServerError") + e.getLocalizedMessage(), e);
}
} else { // default to System
timeProvider = new SystemTimeProvider();
}
CodeGenerator codeGenerator = new DefaultCodeGenerator();
DefaultCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
if (method.getMFAAllowedTimeDiscrepancy() > 0) {
// allow codes valid for the time periods configured before/after to pass as valid (default is 1)
verifier.setAllowedTimePeriodDiscrepancy(method.getMFAAllowedTimeDiscrepancy());
}
boolean valid = verifier.isValidCode(reg.getMFASecret(), code);
if (valid) {
reg.setMFALastSecret(code);
reg.setLastSuccess(new Timestamp(System.currentTimeMillis()));
reg.setFailedLoginCount(0);
} else {
reg.setLastFailure(new Timestamp(System.currentTimeMillis()));
reg.setFailedLoginCount(reg.getFailedLoginCount() + 1);
}
try {
PO.setCrossTenantSafe();
reg.saveEx();
} finally {
PO.clearCrossTenantSafe();
}
return valid;
}
/**
* Generate a validation code - do nothing for TOTP
* @param reg
* @return
*/
@Override
public String generateValidationCode(MMFARegistration reg) {
return Msg.getMsg(Env.getCtx(), "MFATOTPEnterValidationCode");
}
/**
* Validate a code
* @param reg
* @param code
* @param setPreferred
* @return message on error, null when OK
*/
@Override
public String validateCode(MMFARegistration reg, String code, boolean setPreferred) {
Properties ctx = reg.getCtx();
if (code.equals(reg.getMFALastSecret()))
return Msg.getMsg(ctx, "MFACodeAlreadyConsumed");
if (! isValidCode(ctx, reg, code, reg.get_TrxName()))
return Msg.getMsg(ctx, "MFACodeInvalid");
if (setPreferred) {
reg.setIsUserMFAPreferred(true);
try {
PO.setCrossTenantSafe();
reg.saveEx();
} finally {
PO.clearCrossTenantSafe();
}
}
return null;
}
}

View File

@ -97,6 +97,8 @@
<setEntry value="slf4j.api@default:default"/>
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>

View File

@ -94,6 +94,8 @@
<setEntry value="slf4j.api@default:default"/>
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>

View File

@ -94,6 +94,8 @@
<setEntry value="slf4j.api@default:default"/>
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>

View File

@ -370,6 +370,8 @@
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="stax2-api@default:default"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>

View File

@ -371,6 +371,8 @@
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="stax2-api@default:default"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>

View File

@ -79,7 +79,9 @@ osgi.bundles=org.eclipse.equinox.ds@1:start,\
org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec,\
jakarta.xml.bind-api,\
org.eclipse.osgi@start,\
org.dom4j
org.dom4j,\
wrapped.com.google.zxing.javase,\
wrapped.dev.samstevens.totp.totp
osgi.framework.extensions=
osgi.bundles.defaultStartLevel=4
osgi.compatibility.bootdelegation=true

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.adempiere.webui.process.MFARegisterParameterListener">
<implementation class="org.adempiere.webui.process.MFARegisterParameterListener"/>
<property name="ProcessClass" type="String" value="org.compiere.process.MFARegister"/>
<property name="ColumnName" type="String" value="MFA_Method_ID"/>
<service>
<provide interface="org.adempiere.webui.apps.IProcessParameterListener"/>
</service>
</scr:component>

View File

@ -0,0 +1,250 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Sponsor: *
* - FH *
* Contributors: *
* - Carlos Ruiz *
**********************************************************************/
package org.adempiere.webui.apps.form;
import java.sql.Timestamp;
import org.adempiere.webui.component.Checkbox;
import org.adempiere.webui.component.Column;
import org.adempiere.webui.component.Columns;
import org.adempiere.webui.component.ConfirmPanel;
import org.adempiere.webui.component.GridFactory;
import org.adempiere.webui.component.Label;
import org.adempiere.webui.component.Row;
import org.adempiere.webui.component.Rows;
import org.adempiere.webui.component.Textbox;
import org.adempiere.webui.panel.ADForm;
import org.compiere.model.IMFAMechanism;
import org.compiere.model.MMFAMethod;
import org.compiere.model.MMFARegistration;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Grid;
import org.zkoss.zul.Html;
import org.zkoss.zul.Separator;
import org.zkoss.zul.Space;
/**
* IDEMPIERE-4782
* @author Carlos Ruiz - globalqss - BX Service
*/
public class MFARegisterForm extends ADForm {
/**
*
*/
private static final long serialVersionUID = 6186035815377577281L;
/* A label to show the messages to user */
private Html msgInstructions;
/* A label to show the additional messages to user */
private Html msgAdditional;
/* A box to receive the validation code */
private Label codeLabel;
private Textbox codeBox;
/* A box to receive optionally a name */
private Label nameLabel;
private Textbox nameBox;
/* A checkbox to set registration as preferred */
private Checkbox prefBox;
/* The confirm panel to close the window */
private ConfirmPanel confirmPanel;
/* The process instance opening this form */
@SuppressWarnings("unused")
private int pInstanceId;
private MMFARegistration registration = null;
/**
*
*/
public MFARegisterForm() {
}
/* (non-Javadoc)
* @see org.adempiere.webui.panel.ADForm#initForm()
*/
@Override
protected void initForm() {
setClosable(true);
setSizable(true);
Grid grid = GridFactory.newGridLayout();
grid.setHeight("100%");
grid.setWidth("100%");
appendChild(grid);
Columns columns = new Columns();
grid.appendChild(columns);
Column column = new Column();
column.setWidth("5%");
columns.appendChild(column);
column = new Column();
column.setWidth("30%");
columns.appendChild(column);
column = new Column();
column.setWidth("60%");
columns.appendChild(column);
column = new Column();
column.setWidth("10%");
columns.appendChild(column);
Rows rows = new Rows();
grid.appendChild(rows);
Row row = rows.newRow();
row = rows.newRow();
msgInstructions = new Html();
row.appendCellChild(msgInstructions, 4);
msgInstructions.setHflex("1");
row = rows.newRow();
msgAdditional = new Html();
row.appendCellChild(msgAdditional, 4);
msgAdditional.setHflex("1");
row = rows.newRow();
codeLabel = new Label(Msg.getElement(Env.getCtx(), "MFAValidationCode"));
row.appendCellChild(codeLabel, 2);
codeBox = new Textbox();
row.appendCellChild(codeBox, 2);
row = rows.newRow();
nameLabel = new Label(Msg.getElement(Env.getCtx(), "Name"));
row.appendCellChild(nameLabel, 2);
nameBox = new Textbox();
row.appendCellChild(nameBox, 2);
row = rows.newRow();
row.appendCellChild(new Separator(), 2);
prefBox = new Checkbox();
prefBox.setLabel(Msg.getElement(Env.getCtx(), "IsUserMFAPreferred"));
row.appendCellChild(prefBox, 2);
row = rows.newRow();
confirmPanel = new ConfirmPanel(true);
row.appendCellChild(new Space());
row.appendCellChild(confirmPanel, 3);
confirmPanel.addActionListener(Events.ON_CLICK, evt -> onConfirmPanelAction(evt));
//setHeight("600px");
setHeight(null);
setWidth("350px");
setVflex("min");
}
/**
* Set Window Mode Highlighted
*/
@Override
public Mode getWindowMode() {
return Mode.HIGHLIGHTED;
}
/**
* Confirm panel to process OK/Cancel buttons
* @param evt
*/
private void onConfirmPanelAction(Event evt) {
if (evt.getTarget() == confirmPanel.getButton(ConfirmPanel.A_CANCEL)) {
this.detach();
} else if (evt.getTarget() == confirmPanel.getButton(ConfirmPanel.A_OK)) {
if (codeBox.isDisabled())
this.detach();
if (Util.isEmpty(codeBox.getValue()))
throw new WrongValueException(codeBox, Msg.getMsg(Env.getCtx(), "MFACodeRequired"));
Timestamp now = new Timestamp(System.currentTimeMillis());
if (registration.getExpiration() != null && now.after(registration.getExpiration()))
throw new WrongValueException(codeBox, Msg.getMsg(Env.getCtx(), "MFARegistrationExpired"));
MMFAMethod method = new MMFAMethod(Env.getCtx(), registration.getMFA_Method_ID(), registration.get_TrxName());
IMFAMechanism mechanism = method.getMFAMechanism();
String msg;
try {
msg = mechanism.complete(Env.getCtx(), registration, codeBox.getValue(), nameBox.getValue(), prefBox.isChecked(), registration.get_TrxName());
} catch (Exception e) {
String err;
if (! Util.isEmpty(e.getLocalizedMessage()))
err = e.getLocalizedMessage();
else
err = Msg.getMsg(Env.getCtx(), "Error") + " = " + e.toString();
throw new WrongValueException(codeBox, err);
}
codeBox.setDisabled(true);
nameBox.setDisabled(true);
prefBox.setDisabled(true);
msgInstructions.setContent(msg);
msgAdditional.setContent(null);
confirmPanel.getButton(ConfirmPanel.A_CANCEL).setDisabled(true);
confirmPanel.getButton(ConfirmPanel.A_CANCEL).setVisible(false);
}
}
/**
*
* @param retArray
* @param pInstanceId
*/
public void completeRegistration(Object[] retArray, int pInstanceId) {
this.pInstanceId = pInstanceId;
String instructions = retArray[0].toString();
StringBuilder additionalInstructions = new StringBuilder();
for (int i = 1; i < retArray.length; i++) {
if (retArray[i] instanceof String) {
if (additionalInstructions.length() > 0)
additionalInstructions.append("<br>");
String reti = (String) retArray[i];
if (reti.startsWith("data:image/png;base64,"))
additionalInstructions.append("<img src=\"" + reti.toString() + "\" width=\"180\" height=\"180\"/>"); // show QR code
else
additionalInstructions.append(reti.toString());
} else if (retArray[i] instanceof MMFARegistration) {
registration = (MMFARegistration) retArray[i];
}
}
msgInstructions.setContent(instructions);
msgAdditional.setContent(additionalInstructions.toString());
nameBox.setValue(registration.getName());
registration.set_TrxName(null); // this is post-process, the transaction from the process was already closed
}
}

View File

@ -806,7 +806,9 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria
sideController.onLogOut();
sideController = null;
}
if (layout != null) {
layout.detach();
}
layout = null;
pnlHead = null;
max = null;

View File

@ -126,6 +126,9 @@ public class LoginPanel extends Window implements EventListener<Event>
protected boolean email_login = MSysConfig.getBooleanValue(MSysConfig.USE_EMAIL_FOR_LOGIN, false);
protected String validLstLanguage = null;
/* Number of failures to calculate an incremental delay on every trial */
private int failures = 0;
public LoginPanel(Properties ctx, LoginWindow loginWindow)
{
this.ctx = ctx;
@ -592,6 +595,11 @@ public class LoginPanel extends Window implements EventListener<Event>
}
logAuthFailure.log(x_Forward_IP, "/webui", userId, loginErrMsg);
// Incremental delay to avoid brute-force attack on testing codes
try {
Thread.sleep(failures * 2000);
} catch (InterruptedException e) {}
failures++;
Clients.clearBusy();
throw new WrongValueException(loginErrMsg);
}

View File

@ -26,8 +26,6 @@ package org.adempiere.webui.panel;
import java.sql.Timestamp;
import java.util.Properties;
import javax.servlet.http.HttpSession;
import org.adempiere.util.Callback;
import org.adempiere.webui.AdempiereIdGenerator;
import org.adempiere.webui.LayoutUtils;
@ -52,15 +50,12 @@ import org.compiere.util.KeyNamePair;
import org.compiere.util.Language;
import org.compiere.util.Login;
import org.compiere.util.Msg;
import org.compiere.util.TimeUtil;
import org.compiere.util.Util;
import org.zkoss.zhtml.Table;
import org.zkoss.zhtml.Td;
import org.zkoss.zhtml.Tr;
import org.zkoss.zk.au.out.AuFocus;
import org.zkoss.zk.au.out.AuScript;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Session;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Deferrable;
import org.zkoss.zk.ui.event.Event;
@ -85,7 +80,7 @@ public class RolePanel extends Window implements EventListener<Event>, Deferrabl
/**
*
*/
private static final long serialVersionUID = -4763398859555693370L;
private static final long serialVersionUID = -618446343598384819L;
protected LoginWindow wndLogin;
protected Login login;
@ -638,17 +633,6 @@ public class RolePanel extends Window implements EventListener<Event>, Deferrabl
Timestamp date = (Timestamp)lstDate.getValue();
String msg = login.loadPreferences(orgKNPair, warehouseKNPair, date, null);
if (Util.isEmpty(msg))
{
Session currSess = Executions.getCurrent().getDesktop().getSession();
HttpSession httpSess = (HttpSession) currSess.getNativeSession();
int timeout = MSysConfig.getIntValue(MSysConfig.ZK_SESSION_TIMEOUT_IN_SECONDS, -2, Env.getAD_Client_ID(Env.getCtx()), Env.getAD_Org_ID(Env.getCtx()));
if (timeout != -2) // default to -2 meaning not set
httpSess.setMaxInactiveInterval(timeout);
msg = login.validateLogin(orgKNPair);
}
if (! Util.isEmpty(msg))
{
Env.getCtx().clear();
@ -661,38 +645,29 @@ public class RolePanel extends Window implements EventListener<Event>, Deferrabl
return;
}
// See if a popup should encourage user to change its password
if (!MUser.get(Env.getCtx()).isNoPasswordReset()) {
int notifyDay = MSysConfig.getIntValue(MSysConfig.USER_LOCKING_PASSWORD_NOTIFY_DAY, 0);
int pwdAgeDay = MSysConfig.getIntValue(MSysConfig.USER_LOCKING_MAX_PASSWORD_AGE_DAY, 0);
if (notifyDay > 0 && pwdAgeDay > 0) {
Timestamp limit = TimeUtil.addDays(MUser.get(Env.getCtx()).getDatePasswordChanged(), pwdAgeDay);
Timestamp notifyAfter = TimeUtil.addDays(limit, -notifyDay);
Timestamp now = TimeUtil.getDay(null);
if (now.after(notifyAfter))
FDialog.warn(0, null, "", Msg.getMsg(Env.getCtx(), "YourPasswordWillExpireInDays", new Object[] {TimeUtil.getDaysBetween(now, limit)}));
}
}
wndLogin.loginCompleted();
// Elaine 2009/02/06 save preference to AD_Preference
UserPreference userPreference = SessionManager.getSessionApplication().getUserPreference();
userPreference.setProperty(UserPreference.P_LANGUAGE, Env.getContext(m_ctx, UserPreference.LANGUAGE_NAME));
userPreference.setProperty(UserPreference.P_ROLE, (String) lstItemRole.getValue());
userPreference.setProperty(UserPreference.P_CLIENT, (String) lstItemClient.getValue());
userPreference.setProperty(UserPreference.P_ORG, (String) lstItemOrg.getValue());
userPreference.setProperty(UserPreference.P_WAREHOUSE, lstItemWarehouse != null ? (String) lstItemWarehouse.getValue() : "0");
userPreference.setProperty(UserPreference.P_WAREHOUSE,
lstItemWarehouse != null ? (String) lstItemWarehouse.getValue() : "0");
userPreference.savePreference();
// force reload of default role when more than 1 client
if (lstClient.getChildren().size() > 1)
MRole.getDefault(m_ctx, true);
//
wndLogin.validateMFA(orgKNPair);
}
public boolean isDeferrable() {
return false;
}
public boolean show() {
return m_show;
}
}

View File

@ -0,0 +1,439 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - Carlos Ruiz (sponsored by FH) *
**********************************************************************/
package org.adempiere.webui.panel;
import java.sql.Timestamp;
import java.util.Properties;
import java.util.UUID;
import java.util.logging.Level;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.adempiere.util.Callback;
import org.adempiere.util.LogAuthFailure;
import org.adempiere.webui.AdempiereIdGenerator;
import org.adempiere.webui.LayoutUtils;
import org.adempiere.webui.component.ComboItem;
import org.adempiere.webui.component.Combobox;
import org.adempiere.webui.component.ConfirmPanel;
import org.adempiere.webui.component.Label;
import org.adempiere.webui.component.Textbox;
import org.adempiere.webui.component.Window;
import org.adempiere.webui.session.SessionManager;
import org.adempiere.webui.theme.ITheme;
import org.adempiere.webui.theme.ThemeManager;
import org.adempiere.webui.util.ZKUpdateUtil;
import org.adempiere.webui.window.FDialog;
import org.adempiere.webui.window.LoginWindow;
import org.compiere.model.MMFAMethod;
import org.compiere.model.MMFARegisteredDevice;
import org.compiere.model.MMFARegistration;
import org.compiere.model.MSysConfig;
import org.compiere.model.MUser;
import org.compiere.util.CLogger;
import org.compiere.util.Env;
import org.compiere.util.KeyNamePair;
import org.compiere.util.Login;
import org.compiere.util.Msg;
import org.compiere.util.TimeUtil;
import org.compiere.util.Util;
import org.zkoss.zhtml.Div;
import org.zkoss.zhtml.Table;
import org.zkoss.zhtml.Td;
import org.zkoss.zhtml.Tr;
import org.zkoss.zk.au.out.AuFocus;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Session;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Image;
public class ValidateMFAPanel extends Window implements EventListener<Event> {
/**
*
*/
private static final long serialVersionUID = 5521412080450156787L;
private static final CLogger logger = CLogger.getCLogger(ValidateMFAPanel.class);
private static final String ON_DEFER_LOGOUT = "onDeferLogout";
protected LoginWindow wndLogin;
protected Login login;
private ValidateMFAPanel component;
/** Context */
protected Properties m_ctx;
protected boolean m_show = true;
protected Label lblMFAMechanism;
protected Combobox lstMFAMechanism;
protected Label lblMFAMsg;
protected Label lblValidationCode;
protected Textbox txtValidationCode;
protected Checkbox chkSetPreferred;
protected Checkbox chkRegisterDevice = null;
private KeyNamePair m_orgKNPair;
/* Push the first OK automatically - when the first record is TOTP */
private boolean m_autoCall = false;
private static LogAuthFailure logAuthFailure = new LogAuthFailure();
/* Number of failures to calculate an incremental delay on every trial */
private int failures = 0;
public ValidateMFAPanel(Properties ctx, LoginWindow loginWindow, KeyNamePair orgKNPair) {
this.wndLogin = loginWindow;
m_ctx = ctx;
this.m_orgKNPair = orgKNPair;
this.component = this;
String cookieName = Env.getAD_User_ID(m_ctx) + "|" + Env.getAD_Client_ID(m_ctx);
String registerCookie = getCookie(cookieName);
login = new Login(ctx);
if (login.isMFARequired(registerCookie)) {
initComponents();
init();
this.setId("validateMFAPanel");
this.setSclass("login-box");
AuFocus auf = new AuFocus(lstMFAMechanism);
Clients.response(auf);
if (m_autoCall) {
validateMFAComplete(true);
}
} else {
if (logger.isLoggable(Level.INFO)) logger.info("MFA not required");
validateMFAComplete(false);
}
}
private void init() {
Div div = new Div();
div.setSclass(ITheme.LOGIN_BOX_HEADER_CLASS);
Label label = new Label(Msg.getMsg(m_ctx, "MFALoginValidationHeader"));
label.setSclass(ITheme.LOGIN_BOX_HEADER_TXT_CLASS);
div.appendChild(label);
this.appendChild(div);
Table table = new Table();
table.setId("grdMFAValidate");
table.setDynamicProperty("cellpadding", "0");
table.setDynamicProperty("cellspacing", "5");
table.setSclass(ITheme.LOGIN_BOX_BODY_CLASS);
this.appendChild(table);
Tr tr = new Tr();
table.appendChild(tr);
Td td = new Td();
td.setSclass(ITheme.LOGIN_BOX_HEADER_LOGO_CLASS);
tr.appendChild(td);
td.setDynamicProperty("colspan", "2");
Image image = new Image();
image.setSrc(ThemeManager.getLargeLogo());
td.appendChild(image);
tr = new Tr();
tr.setId("rowMFAMechanism");
table.appendChild(tr);
td = new Td();
tr.appendChild(td);
td.setSclass(ITheme.LOGIN_LABEL_CLASS);
td.appendChild(lblMFAMechanism);
td = new Td();
td.setSclass(ITheme.LOGIN_FIELD_CLASS);
tr.appendChild(td);
td.appendChild(lstMFAMechanism);
tr = new Tr();
tr.setId("rowMFAMsg");
table.appendChild(tr);
td = new Td();
tr.appendChild(td);
td.setSclass(ITheme.LOGIN_LABEL_CLASS);
td.appendChild(new Label(""));
td = new Td();
tr.appendChild(td);
td.setSclass(ITheme.LOGIN_FIELD_CLASS);
td.appendChild(lblMFAMsg);
tr = new Tr();
tr.setId("rowValidationCode");
table.appendChild(tr);
td = new Td();
tr.appendChild(td);
td.setSclass(ITheme.LOGIN_LABEL_CLASS);
td.appendChild(lblValidationCode);
td = new Td();
td.setSclass(ITheme.LOGIN_FIELD_CLASS);
tr.appendChild(td);
td.appendChild(txtValidationCode);
tr = new Tr();
tr.setId("rowSetPreferred");
table.appendChild(tr);
td = new Td();
tr.appendChild(td);
td.setSclass(ITheme.LOGIN_LABEL_CLASS);
td.appendChild(new Label(""));
td = new Td();
td.setSclass(ITheme.LOGIN_FIELD_CLASS);
tr.appendChild(td);
td.appendChild(chkSetPreferred);
tr = new Tr();
tr.setId("rowRegisterDevice");
table.appendChild(tr);
td = new Td();
tr.appendChild(td);
td.setSclass(ITheme.LOGIN_LABEL_CLASS);
td.appendChild(new Label(""));
td = new Td();
td.setSclass(ITheme.LOGIN_FIELD_CLASS);
tr.appendChild(td);
td.appendChild(chkRegisterDevice);
div = new Div();
div.setSclass(ITheme.LOGIN_BOX_FOOTER_CLASS);
ConfirmPanel pnlButtons = new ConfirmPanel(true);
pnlButtons.addActionListener(this);
LayoutUtils.addSclass(ITheme.LOGIN_BOX_FOOTER_PANEL_CLASS, pnlButtons);
ZKUpdateUtil.setWidth(pnlButtons, null);
pnlButtons.getButton(ConfirmPanel.A_OK).setSclass(ITheme.LOGIN_BUTTON_CLASS);
pnlButtons.getButton(ConfirmPanel.A_CANCEL).setSclass(ITheme.LOGIN_BUTTON_CLASS);
div.appendChild(pnlButtons);
this.appendChild(div);
}
private void initComponents() {
lblMFAMechanism = new Label();
lblMFAMechanism.setId("lblMFAMechanism");
lblMFAMechanism.setValue(Msg.getMsg(m_ctx, "MFALoginMechanism"));
lblValidationCode = new Label();
lblValidationCode.setId("lblValidationCode");
lblValidationCode.setValue(Msg.getMsg(m_ctx, "MFALoginValidationCode"));
lblMFAMsg = new Label();
lblMFAMsg.setId("lblMFAMsg");
lblMFAMsg.setValue(Msg.getMsg(m_ctx, "MFALoginMessage"));
lstMFAMechanism = new Combobox();
lstMFAMechanism.setAutocomplete(true);
lstMFAMechanism.setAutodrop(true);
lstMFAMechanism.setId("lstMFAMechanism");
boolean first = true;
for (MMFARegistration reg : MMFARegistration.getValidRegistrationsFromUser()) {
MMFAMethod method = new MMFAMethod(m_ctx, reg.getMFA_Method_ID(), reg.get_TrxName());
if (first) {
first = false;
if (MMFAMethod.METHOD_Time_BasedOne_TimePassword.equals(method.getMethod())) {
m_autoCall = true;
}
}
ComboItem ci = new ComboItem(reg.getName() + " - " + method.getMethod(), reg.getMFA_Registration_ID());
String id = AdempiereIdGenerator.escapeId(ci.getLabel());
if (lstMFAMechanism.getFellowIfAny(id) == null)
ci.setId(id);
lstMFAMechanism.appendChild(ci);
}
lstMFAMechanism.setSelectedIndex(0);
ZKUpdateUtil.setWidth(lstMFAMechanism, "220px");
chkSetPreferred = new Checkbox(Msg.getMsg(m_ctx, "MFALoginSetPreferred"));
chkSetPreferred.setId("chkSetPreferred");
boolean enablePreferred = (lstMFAMechanism.getChildren().size() > 1 && lstMFAMechanism.getSelectedIndex() > 0);
chkSetPreferred.setVisible(enablePreferred);
chkSetPreferred.setChecked(false);
int daysExpire = MSysConfig.getIntValue(MSysConfig.MFA_REGISTERED_DEVICE_EXPIRATION_DAYS, 30, Env.getAD_Client_ID(m_ctx));
chkRegisterDevice = new Checkbox(Msg.getMsg(m_ctx, "MFALoginRegisterDevice", new Object[] {daysExpire}));
chkRegisterDevice.setId("chkRegisterDevice");
boolean enableRegisterDevice = (daysExpire > 0);
chkRegisterDevice.setVisible(enableRegisterDevice);
chkRegisterDevice.setChecked(false);
txtValidationCode = new Textbox();
txtValidationCode.setId("txtValidationCode");
txtValidationCode.setCols(25);
ZKUpdateUtil.setWidth(txtValidationCode, "220px");
txtValidationCode.setDisabled(true);
this.addEventListener(ON_DEFER_LOGOUT, this);
}
public void onEvent(Event event) {
if (event.getTarget().getId().equals(ConfirmPanel.A_OK)) {
validateMFAComplete(true);
} else if (event.getTarget().getId().equals(ConfirmPanel.A_CANCEL)) {
SessionManager.logoutSession();
} else if (ON_DEFER_LOGOUT.equals(event.getName())) {
SessionManager.logoutSession();
}
}
public void validateMFAComplete(boolean required) {
Clients.clearBusy();
int registrationId = 0;
if (required) {
registrationId = lstMFAMechanism.getSelectedItem().getValue();
boolean enablePreferred = (lstMFAMechanism.getChildren().size() > 1 && lstMFAMechanism.getSelectedIndex() > 0);
chkSetPreferred.setVisible(enablePreferred);
MMFARegistration reg = new MMFARegistration(Env.getCtx(), registrationId, null);
if (txtValidationCode.isDisabled()) {
String msg = reg.generateValidationCode(reg);
lblMFAMsg.setValue(msg);
txtValidationCode.setDisabled(false);
lstMFAMechanism.setDisabled(true);
AuFocus auf = new AuFocus(txtValidationCode);
Clients.response(auf);
return;
} else {
if (Util.isEmpty(txtValidationCode.getText()) && lstMFAMechanism.getItemCount() > 1) {
lblMFAMsg.setValue(Msg.getMsg(m_ctx, "MFALoginMessage"));
txtValidationCode.setDisabled(true);
lstMFAMechanism.setDisabled(false);
AuFocus auf = new AuFocus(lstMFAMechanism);
Clients.response(auf);
return;
} else {
String msg = reg.validateCode(reg, txtValidationCode.getText(), chkSetPreferred.isChecked());
if (msg != null) {
String x_Forward_IP = Executions.getCurrent().getHeader("X-Forwarded-For");
if (x_Forward_IP == null)
x_Forward_IP = Executions.getCurrent().getRemoteAddr();
MUser user = MUser.get(m_ctx);
boolean email_login = MSysConfig.getBooleanValue(MSysConfig.USE_EMAIL_FOR_LOGIN, false);
logAuthFailure.log(x_Forward_IP, "/webui", email_login ? user.getEMail() : user.getName(), msg);
// Incremental delay to avoid brute-force attack on testing codes
try {
Thread.sleep(failures * 2000);
} catch (InterruptedException e) {}
failures++;
AuFocus auf = new AuFocus(txtValidationCode);
Clients.response(auf);
throw new WrongValueException(txtValidationCode, msg);
}
}
}
}
if (chkRegisterDevice != null && chkRegisterDevice.isChecked()) {
String cookieName = Env.getAD_User_ID(m_ctx) + "|" + Env.getAD_Client_ID(m_ctx);
// TODO: generate the random cookie if possible with some fingerprint of the device
String cookieValue = UUID.randomUUID().toString();
setCookie(cookieName, cookieValue);
MMFARegisteredDevice rd = new MMFARegisteredDevice(m_ctx, 0, null);
rd.setAD_User_ID(Env.getAD_User_ID(m_ctx));
rd.setMFADeviceIdentifier(cookieValue);
long daysExpire = MSysConfig.getIntValue(MSysConfig.MFA_REGISTERED_DEVICE_EXPIRATION_DAYS, 30, Env.getAD_Client_ID(m_ctx));
rd.setExpiration(new Timestamp(System.currentTimeMillis() + (daysExpire * 86400000L)));
// TODO: rd.setHelp -> add information about the browser, device and IP address (fingerprint)
rd.saveEx();
}
Session currSess = Executions.getCurrent().getDesktop().getSession();
HttpSession httpSess = (HttpSession) currSess.getNativeSession();
int timeout = MSysConfig.getIntValue(MSysConfig.ZK_SESSION_TIMEOUT_IN_SECONDS, -2,
Env.getAD_Client_ID(Env.getCtx()), Env.getAD_Org_ID(Env.getCtx()));
if (timeout != -2) // default to -2 meaning not set
httpSess.setMaxInactiveInterval(timeout);
String msg = login.validateLogin(m_orgKNPair);
if (!Util.isEmpty(msg)) {
Env.getCtx().clear();
FDialog.error(0, this, "Error", msg, new Callback<Integer>() {
@Override
public void onCallback(Integer result) {
Events.echoEvent(new Event(ON_DEFER_LOGOUT, component));
}
});
return;
}
// See if a popup should encourage user to change its password
if (!MUser.get(Env.getCtx()).isNoPasswordReset()) {
int notifyDay = MSysConfig.getIntValue(MSysConfig.USER_LOCKING_PASSWORD_NOTIFY_DAY, 0);
int pwdAgeDay = MSysConfig.getIntValue(MSysConfig.USER_LOCKING_MAX_PASSWORD_AGE_DAY, 0);
if (notifyDay > 0 && pwdAgeDay > 0) {
Timestamp limit = TimeUtil.addDays(MUser.get(Env.getCtx()).getDatePasswordChanged(), pwdAgeDay);
Timestamp notifyAfter = TimeUtil.addDays(limit, -notifyDay);
Timestamp now = TimeUtil.getDay(null);
if (now.after(notifyAfter))
FDialog.warn(0, null, "", Msg.getMsg(m_ctx, "YourPasswordWillExpireInDays",
new Object[] { TimeUtil.getDaysBetween(now, limit) }));
}
}
Env.setContext(m_ctx, "#MFA_Registration_ID", registrationId);
wndLogin.loginCompleted();
}
/**
* Set a cookie
* @param name
* @param value
*/
public static void setCookie(String name, String value) {
Cookie cookie = new Cookie(name, value);
cookie.setSecure(true);
((HttpServletResponse) Executions.getCurrent().getNativeResponse()).addCookie(cookie);
}
/**
* Get a cookie by name
* @param name
* @return
*/
public static String getCookie(String name) {
Cookie[] cookies = ((HttpServletRequest) Executions.getCurrent().getNativeRequest()).getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(name)) {
return cookie.getValue();
}
}
}
return null;
}
}

View File

@ -0,0 +1,68 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Sponsor: *
* - FH *
* Contributors: *
* - Carlos Ruiz *
**********************************************************************/
package org.adempiere.webui.process;
import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.apps.form.MFARegisterForm;
import org.adempiere.webui.panel.ADForm;
import org.adempiere.webui.session.SessionManager;
import org.adempiere.webui.util.IServerPushCallback;
import org.adempiere.webui.util.ServerPushTemplate;
import org.compiere.model.SystemIDs;
import org.zkoss.zk.ui.Desktop;
/**
* IDEMPIERE-4782
* @author Carlos Ruiz - globalqss - BX Service
*/
public class MFARegister extends org.compiere.process.MFARegister implements IServerPushCallback {
/**
* Post process to register the server push callback
*/
@Override
protected void postProcess(boolean success) {
if (success) {
Desktop desktop = AEnv.getDesktop();
ServerPushTemplate template = new ServerPushTemplate(desktop);
template.executeAsync(this);
}
}
/**
* Open the complete registration form when updating the UI on server push callback
*/
@Override
public void updateUI() {
ADForm form = SessionManager.getAppDesktop().openForm(SystemIDs.FORM_MFA_REGISTER);
if (form instanceof MFARegisterForm) {
((MFARegisterForm)form).completeRegistration(retArray, getAD_PInstance_ID());
}
}
} // MFARegister

View File

@ -0,0 +1,58 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Sponsor: *
* - FH *
* Contributors: *
* - Carlos Ruiz *
**********************************************************************/
package org.adempiere.webui.process;
import org.adempiere.webui.apps.IProcessParameterListener;
import org.adempiere.webui.apps.ProcessParameterPanel;
import org.adempiere.webui.editor.WEditor;
import org.compiere.model.MMFAMethod;
import org.compiere.util.Env;
public class MFARegisterParameterListener implements IProcessParameterListener {
@Override
public void onChange(ProcessParameterPanel parameterPanel, String columnName, WEditor editor) {
if (editor.getValue() != null) {
if ("MFA_Method_ID".equals(columnName)) {
Object value = editor.getValue();
if (value != null && value instanceof Integer) {
WEditor elementPrmEditor = parameterPanel.getEditor("MFA_ElementPrm_ID");
Integer methodId = (Integer) value;
if (methodId.intValue() > 0) {
MMFAMethod method = new MMFAMethod(Env.getCtx(), methodId, null);
// set Accounting Date when StatementDate changes
elementPrmEditor.setValue(method.getMFA_ElementPrm_ID());
} else {
elementPrmEditor.setValue(null);
}
}
}
}
}
}

View File

@ -43,9 +43,13 @@ public class SessionManager
String adRoleId = Env.getContext(ctx, Env.AD_ROLE_ID);
String adClientId = Env.getContext(ctx, Env.AD_CLIENT_ID);
String adOrgId = Env.getContext(ctx, Env.AD_ORG_ID);
String mfaId = Env.getContext(ctx, "#MFA_Registration_ID");
return (!"".equals(adUserId) && !"".equals(adRoleId)
&& !"".equals(adClientId) && !"".equals(adOrgId));
return ( !"".equals(mfaId)
&& !"".equals(adOrgId)
&& !"".equals(adUserId)
&& !"".equals(adRoleId)
&& !"".equals(adClientId));
}
public static void setSessionApplication(IWebClient app)

View File

@ -110,6 +110,7 @@ public final class UserPreference implements Serializable {
String attribute = PROPERTIES[i];
String value = props.getProperty(attribute);
if (value != null) {
MPreference preference = query.setParameters(new Object[]{m_AD_User_ID, attribute}).firstOnly();
if (preference == null) {
preference = new MUserPreference(Env.getCtx(), 0, null);
@ -131,6 +132,7 @@ public final class UserPreference implements Serializable {
}
}
}
}
/**
* load user preference

View File

@ -32,6 +32,7 @@ import org.adempiere.webui.panel.ChangePasswordPanel;
import org.adempiere.webui.panel.LoginPanel;
import org.adempiere.webui.panel.ResetPasswordPanel;
import org.adempiere.webui.panel.RolePanel;
import org.adempiere.webui.panel.ValidateMFAPanel;
import org.adempiere.webui.session.SessionContextListener;
import org.adempiere.webui.theme.ThemeManager;
import org.compiere.model.MSysConfig;
@ -61,13 +62,14 @@ public class LoginWindow extends FWindow implements EventListener<Event>
/**
*
*/
private static final long serialVersionUID = -5169830531440825871L;
private static final long serialVersionUID = 2783026164782754864L;
protected IWebClient app;
protected Properties ctx;
protected LoginPanel pnlLogin;
protected ResetPasswordPanel pnlResetPassword;
protected ChangePasswordPanel pnlChangePassword;
protected ValidateMFAPanel pnlValidateMFA = null;
protected RolePanel pnlRole;
public LoginWindow() {}
@ -97,7 +99,10 @@ public class LoginWindow extends FWindow implements EventListener<Event>
{
createRolePanel(userName, show, clientsKNPairs);
this.getChildren().clear();
if (pnlRole.show())
this.appendChild(pnlRole);
else
this.appendChild(pnlValidateMFA);
}
protected void createRolePanel(String userName, boolean show,
@ -130,6 +135,18 @@ public class LoginWindow extends FWindow implements EventListener<Event>
pnlResetPassword = new ResetPasswordPanel(ctx, this, userName, noSecurityQuestion);
}
public void validateMFA(KeyNamePair orgKNPair) {
Clients.clearBusy();
createValidateMFAPanel(orgKNPair);
this.getChildren().clear();
this.appendChild(pnlValidateMFA);
}
private void createValidateMFAPanel(KeyNamePair orgKNPair) {
if (pnlValidateMFA == null)
pnlValidateMFA = new ValidateMFAPanel(ctx, this, orgKNPair);
}
public void loginCompleted()
{
app.loginCompleted();
@ -178,6 +195,12 @@ public class LoginWindow extends FWindow implements EventListener<Event>
resetPasswordPanel.validate();
return;
}
ValidateMFAPanel validateMFAPanel = (ValidateMFAPanel)this.getFellowIfAny("validateMFAPanel");
if (validateMFAPanel != null){
validateMFAPanel.validateMFAComplete(true);
return;
}
}
}
@ -189,6 +212,8 @@ public class LoginWindow extends FWindow implements EventListener<Event>
public void changeRole(Locale locale, Properties ctx)
{
Env.setCtx(ctx);
// clear the org ID - to force a logout if the user pushes Refresh on RolePanel
Env.setContext(ctx, Env.AD_ORG_ID, "");
getDesktop().getSession().setAttribute(SessionContextListener.SESSION_CTX, ctx);
//reload theme preference

View File

@ -162,4 +162,19 @@
<version>1.1.0.Final</version>
<type>jar</type>
</location>
<location includeSource="true" missingManifest="generate"
type="Maven">
<groupId>dev.samstevens.totp</groupId>
<artifactId>totp</artifactId>
<version>1.7.1</version>
<type>jar</type>
</location>
<location includeSource="true" missingManifest="generate"
type="Maven">
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
<type>jar</type>
</location>
</locations>

View File

@ -465,5 +465,19 @@
<version>1.1.0.Final</version>
<type>jar</type>
</location>
<location includeSource="true" missingManifest="generate"
type="Maven">
<groupId>dev.samstevens.totp</groupId>
<artifactId>totp</artifactId>
<version>1.7.1</version>
<type>jar</type>
</location>
<location includeSource="true" missingManifest="generate"
type="Maven">
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
<type>jar</type>
</location>
</locations>
</target>

View File

@ -346,6 +346,8 @@
<setEntry value="slf4j.jcl@default:false"/>
<setEntry value="stax2-api@default:default"/>
<setEntry value="wrapped.com.google.http-client.google-http-client-gson@default:default"/>
<setEntry value="wrapped.com.google.zxing.javase@default:default"/>
<setEntry value="wrapped.dev.samstevens.totp.totp@default:default"/>
<setEntry value="wrapped.io.grpc.grpc-context@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-api@default:default"/>
<setEntry value="wrapped.io.opencensus.opencensus-contrib-http-util@default:default"/>