Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3153 login math #4193

Merged
merged 4 commits into from
Oct 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -437,13 +437,6 @@ Place this ``user-add.json`` file in your current directory and run the followin

curl -d @user-add.json -H "Content-type:application/json" "$SERVER_URL/api/builtin-users?password=$NEWUSER_PASSWORD&key=$BUILTIN_USERS_KEY"

Retrieving the API Token of a Builtin User
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

To retrieve the API token of a builtin user, given that user's password, use the curl command below::

curl "$SERVER_URL/api/builtin-users/$DV_USER_NAME/api-token?password=$DV_USER_PASSWORD"

Roles
~~~~~

Expand Down
7 changes: 7 additions & 0 deletions doc/sphinx-guides/source/installation/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1204,3 +1204,10 @@ You can replace the default dataset metadata fields that are displayed above fil
``curl http://localhost:8080/api/admin/settings/:CustomDatasetSummaryFields -X PUT -d 'producer,subtitle,alternativeTitle'``

You have to put the datasetFieldType name attribute in the :CustomDatasetSummaryFields setting for this to work.

:AllowApiTokenLookupViaApi
++++++++++++++++++++++++++

Dataverse 4.8.1 and below allowed API Token lookup via API but for better security this has been disabled by default. Set this to true if you really want the old behavior.

``curl -X PUT -d 'true' http://localhost:8080/api/admin/settings/:AllowApiTokenLookupViaApi``
54 changes: 54 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/LoginPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.validator.ValidatorException;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
Expand Down Expand Up @@ -101,6 +104,11 @@ public enum EditMode {LOGIN, SUCCESS, FAILED};

private String redirectPage = "dataverse.xhtml";
private AuthenticationProvider authProvider;
private int numFailedLoginAttempts;
Random random;
long op1;
long op2;
Long userSum;

public void init() {
Iterator<String> credentialsIterator = authSvc.getAuthenticationProviderIdsOfType( CredentialsAuthenticationProvider.class ).iterator();
Expand All @@ -109,6 +117,7 @@ public void init() {
}
resetFilledCredentials(null);
authProvider = authSvc.getAuthenticationProvider(systemConfig.getDefaultAuthProvider());
random = new Random();
}

public List<AuthenticationProviderDisplayInfo> listCredentialsAuthenticationProviders() {
Expand Down Expand Up @@ -179,6 +188,9 @@ public String login() {


} catch (AuthenticationFailedException ex) {
numFailedLoginAttempts++;
op1 = new Long(random.nextInt(10));
op2 = new Long(random.nextInt(10));
AuthenticationResponse response = ex.getResponse();
switch ( response.getStatus() ) {
case FAIL:
Expand Down Expand Up @@ -256,4 +268,46 @@ public String getLoginButtonText() {
return BundleUtil.getStringFromBundle("login.button", Arrays.asList("???"));
}
}

public int getNumFailedLoginAttempts() {
return numFailedLoginAttempts;
}

public boolean isRequireExtraValidation() {
if (numFailedLoginAttempts > 2) {
return true;
} else {
return false;
}
}

public long getOp1() {
return op1;
}

public long getOp2() {
return op2;
}

public Long getUserSum() {
return userSum;
}

public void setUserSum(Long userSum) {
this.userSum = userSum;
}

// TODO: Consolidate with SendFeedbackDialog.validateUserSum?
public void validateUserSum(FacesContext context, UIComponent component, Object value) throws ValidatorException {
// The FacesMessage text is on the xhtml side.
FacesMessage msg = new FacesMessage("");
ValidatorException validatorException = new ValidatorException(msg);
if (value == null) {
throw validatorException;
}
if (op1 + op2 != (Long) value) {
throw validatorException;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class SendFeedbackDialog implements java.io.Serializable {
private String userMessage = "";
private String messageSubject = "";
private String messageTo = "";
// FIXME: Remove "support@thedata.org". There's no reason to email the Dataverse *project*. People should email the *installation* instead.
private String defaultRecipientEmail = "support@thedata.org";
Long op1, op2, userSum;
// Either the dataverse or the dataset that the message is pertaining to
Expand Down Expand Up @@ -161,6 +162,7 @@ public void validateUserSum(FacesContext context, UIComponent component, Object

if (op1 + op2 !=(Long)value) {

// TODO: Remove this English "Sum is incorrect" string. contactFormFragment.xhtml uses contact.sum.invalid instead.
FacesMessage msg
= new FacesMessage("Sum is incorrect, please try again.");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/api/BuiltinUsers.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import edu.harvard.iq.dataverse.authorization.providers.builtin.PasswordEncryption;
import edu.harvard.iq.dataverse.authorization.users.ApiToken;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.logging.Level;
Expand Down Expand Up @@ -53,6 +54,14 @@ public class BuiltinUsers extends AbstractApiBean {
@GET
@Path("{username}/api-token")
public Response getApiToken( @PathParam("username") String username, @QueryParam("password") String password ) {
boolean disabled = true;
boolean lookupAllowed = settingsSvc.isTrueForKey(SettingsServiceBean.Key.AllowApiTokenLookupViaApi, false);
if (lookupAllowed) {
disabled = false;
}
if (disabled) {
return error(Status.FORBIDDEN, "This API endpoint has been disabled.");
}
BuiltinUser u = null;
if (retrievingApiTokenViaEmailEnabled) {
u = builtinUserSvc.findByUsernameOrEmail(username);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class SettingsServiceBean {
* So there.
*/
public enum Key {
AllowApiTokenLookupViaApi,
/**
* Ordered, comma-separated list of custom fields to show above the fold
* on dataset page such as "data_type,sample,pdb"
Expand Down
17 changes: 17 additions & 0 deletions src/main/webapp/loginpage.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,23 @@
</div>
</div>
</ui:repeat>

<!-- validation -->
<div class="form-group" jsf:rendered="#{LoginPage.requireExtraValidation}">
<div class="col-sm-offset-4 col-sm-8">
<p>
<h:outputText styleClass="highlightBold" value="#{bundle['contact.question']}"/> <span class="glyphicon glyphicon-asterisk text-danger" title="#{bundle.requiredField}"/>
</p>
<h:outputFormat value="#{LoginPage.op1} + #{LoginPage.op2} = "/>
<p:inputText id="messageSum" label="Sum" size="4" value="#{LoginPage.userSum}" converterMessage="#{bundle['contact.sum.converterMessage']}"
required="#{param['DO_VALIDATION']}" requiredMessage="#{bundle['contact.sum.required']}"
validatorMessage="#{bundle['contact.sum.invalid']}" validator="#{LoginPage.validateUserSum}">
<f:convertNumber integerOnly="true" type="number"/>
</p:inputText>
<h:message for="messageSum" styleClass="bg-danger text-danger"/>
</div>
</div>

<div class="form-group">
<div class="col-sm-offset-4 col-sm-9 button-block">
<p:commandButton id="login" styleClass="btn btn-default" value="#{bundle.login}" update="@all" action="#{LoginPage.login}"/>
Expand Down
19 changes: 19 additions & 0 deletions src/test/java/edu/harvard/iq/dataverse/api/BuiltinUsersIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import javax.json.Json;
import javax.json.JsonObjectBuilder;
import static javax.ws.rs.core.Response.Status.OK;
import static javax.ws.rs.core.Response.Status.FORBIDDEN;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.startsWith;
Expand All @@ -37,6 +38,11 @@ public class BuiltinUsersIT {
@BeforeClass
public static void setUp() {
RestAssured.baseURI = UtilIT.getRestAssuredBaseUri();

Response removeIdentifierGenerationStyle = UtilIT.deleteSetting(SettingsServiceBean.Key.AllowApiTokenLookupViaApi);
removeIdentifierGenerationStyle.then().assertThat()
.statusCode(200);

}

@Test
Expand Down Expand Up @@ -171,6 +177,15 @@ public void testLogin() {
String createdToken = createdUser.getString("data.apiToken");
logger.info(createdToken);

Response getApiTokenShouldFail = getApiTokenUsingUsername(usernameToCreate, usernameToCreate);
getApiTokenShouldFail.then().assertThat()
.body("message", equalTo("This API endpoint has been disabled."))
.statusCode(FORBIDDEN.getStatusCode());

Response setAllowApiTokenLookupViaApi = UtilIT.setSetting(SettingsServiceBean.Key.AllowApiTokenLookupViaApi, "true");
setAllowApiTokenLookupViaApi.then().assertThat()
.statusCode(OK.getStatusCode());

Response getApiTokenUsingUsername = getApiTokenUsingUsername(usernameToCreate, usernameToCreate);
getApiTokenUsingUsername.prettyPrint();
assertEquals(200, getApiTokenUsingUsername.getStatusCode());
Expand All @@ -189,6 +204,10 @@ public void testLogin() {
assertEquals(createdToken, retrievedTokenUsingEmail);
}

Response removeIdentifierGenerationStyle = UtilIT.deleteSetting(SettingsServiceBean.Key.AllowApiTokenLookupViaApi);
removeIdentifierGenerationStyle.then().assertThat()
.statusCode(200);

}

@Test
Expand Down