Skip to content
Open
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
1 change: 1 addition & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ on:
options:
- bfaso
- boad
- cameroontrubudget
- chad
- civ
- drc
Expand Down
9 changes: 6 additions & 3 deletions amp/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ RUN npm run build \
&& rm -rf node_modules
FROM maven:3.8.4-jdk-8 as compile-mvn
WORKDIR /tmp/amp
# Copy Maven retry helper
COPY docker/mvn-with-retry.sh /usr/local/bin/mvn-with-retry.sh
RUN chmod +x /usr/local/bin/mvn-with-retry.sh
# Copy pom.xml first for better Maven caching
COPY pom.xml .
#COPY amp/pom.xml amp/
Expand All @@ -210,21 +213,21 @@ COPY --from=compile-reampv2 /tmp/amp/TEMPLATE/reampv2 TEMPLATE/reampv2
# Download Maven dependencies (this layer will be cached if pom.xml doesn't change)
ARG BUILD_SOURCE
RUN --mount=type=cache,target=/root/.m2 \
mvn -B dependency:go-offline -f pom.xml || true
mvn-with-retry.sh -B -Dmaven.wagon.http.retryHandler.count=5 dependency:go-offline -f pom.xml || true
# Copy source code after dependencies are downloaded
COPY . .
ARG SKIP_TESTS=false
RUN --mount=type=cache,target=/root/.m2 \
if [ "$SKIP_TESTS" = "true" ]; then \
mvn -B clean compile war:exploded \
mvn-with-retry.sh -B -Dmaven.wagon.http.retryHandler.count=5 clean compile war:exploded \
-DbuildSource=$BUILD_SOURCE \
-Djdbc.user=amp -Djdbc.password=amp122006 -Djdbc.db=amp -Djdbc.host=db \
-Djdbc.port=5432 -DdbName=postgresql -Djdbc.driverClassName=org.postgresql.Driver \
-Dskip.npm -Dskip.installnodenpm -DskipTests \
&& mv target/amp exploded \
&& rm -rf target; \
else \
mvn -B test war:exploded \
mvn-with-retry.sh -B -Dmaven.wagon.http.retryHandler.count=5 test war:exploded \
-DbuildSource=$BUILD_SOURCE \
-Djdbc.user=amp -Djdbc.password=amp122006 -Djdbc.db=amp -Djdbc.host=db \
-Djdbc.port=5432 -DdbName=postgresql -Djdbc.driverClassName=org.postgresql.Driver \
Expand Down
166 changes: 89 additions & 77 deletions amp/TEMPLATE/ampTemplate/js_2/jdigestauth/digest-auth.js
Original file line number Diff line number Diff line change
@@ -1,90 +1,102 @@
/*
* A JavaScript implementation of the Digest Authentication
* Digest Authentication, as defined in RFC 2617.
* Version 1.0 Copyright (C) Maricn Michalski (http://marcin-michalski.pl)
* Distributed under the BSD License
*
* site: http://arrowgroup.eu
* Login widget submission handler.
* Sends the SHA-1 hash of the password (never the plaintext) to /aim/postLogin.do,
* which verifies it the same way as the /rest/security/user API login.
*/

function getCsrfTokenFromCookie() {
var match = document.cookie.match(/(?:^|;\s*)XSRF-TOKEN=([^;]*)/);
return match ? decodeURIComponent(match[1]) : null;
}

function ajaxLogin() {
$('#loader').show();

var digestAuth = new pl.arrowgroup.DigestAuthentication(
{
onSuccess : function(data) {
var serverResponse=JSON.parse(data);
var error = jQuery.trim(serverResponse.original_result);
var reportError = function(id) {
$('#loader').hide();
$('#result').show();
$(".error_text_login > span").hide();
$("#" + id).show();
};

var handleResponse = function(data) {
var serverResponse = JSON.parse(data);
var error = jQuery.trim(serverResponse.original_result);

$('#result').hide();

//Suspended login
var suspendReasons = [];
if (error != null && error.length > 13) {
if (error.substring (0, 13) == "userSuspended") {
//split reasons
var startIndex = 0;
var endIndex = 0;

$('#result').hide();

//Suspended login
var suspendedLoginText = null;
var suspendReasons = [];
if (error != null && error.length > 13) {
if (error.substring (0, 13) == "userSuspended") {
//split reasons
var startIndex = 0;
var endIndex = 0;

while (endIndex != error.length - 1) {
startIndex = error.indexOf("{", endIndex);
endIndex = error.indexOf("}", startIndex);
var reasonTxt = error.substring (startIndex + 1, endIndex);
suspendReasons.push(reasonTxt);
}
}
error = "userSuspended";
}

//endOf Suspended login

switch (error) {
case 'noTeamMember':
reportError("unassigned_user");
break;
case 'userBanned':
reportError("banned_user");
break;
case 'invalidUser':
// isn't the generic onFailure actually called for this use case?
reportError("invalid_user");
break;
case 'userSuspended':
var suspUserErrTxt = "";
var reasonIdx = 0;
for (reasonIdx = 0; reasonIdx < suspendReasons.length; reasonIdx ++){
suspUserErrTxt += suspendReasons[reasonIdx];
if (reasonIdx < suspendReasons.length) {
suspUserErrTxt += "<br />"
}
}
$('#suspend').html(suspUserErrTxt);
reportError("suspend");
break;
case 'noError':
location.href = '/index.do';
break;
}
},
onFailure : function(response){
reportError("invalid_user_pwd");
},
cnonce : 'testCnonce'
}
);

var reportError = function(id) {
$('#loader').hide();
$('#result').show();
$(".error_text_login > span").hide();
$("#" + id).show();
};
while (endIndex != error.length - 1) {
startIndex = error.indexOf("{", endIndex);
endIndex = error.indexOf("}", startIndex);
var reasonTxt = error.substring (startIndex + 1, endIndex);
suspendReasons.push(reasonTxt);
}
}
error = "userSuspended";
}

digestAuth.setCredentials($('#j_username').val().trim(),$('#j_password').val());
digestAuth.call('/aim/postLogin.do');
//endOf Suspended login

switch (error) {
case 'noTeamMember':
reportError("unassigned_user");
break;
case 'userBanned':
reportError("banned_user");
break;
case 'invalidUser':
reportError("invalid_user_pwd");
break;
case 'userSuspended':
var suspUserErrTxt = "";
var reasonIdx = 0;
for (reasonIdx = 0; reasonIdx < suspendReasons.length; reasonIdx ++){
suspUserErrTxt += suspendReasons[reasonIdx];
if (reasonIdx < suspendReasons.length) {
suspUserErrTxt += "<br />"
}
}
$('#suspend').html(suspUserErrTxt);
reportError("suspend");
break;
case 'noError':
location.href = '/index.do';
break;
}
};

$.ajax({
url: '/aim/postLogin.do',
type: 'POST',
cache: false,
// this page may not load common.js (e.g. the standalone publicPortalLogin.jsp), so
// don't rely on its global CSRF header injection — attach it here directly
beforeSend: function(xhr) {
var token = getCsrfTokenFromCookie();
if (token) {
xhr.setRequestHeader('X-XSRF-TOKEN', token);
}
},
data: {
j_username: $('#j_username').val().trim(),
j_password: CryptoJS.SHA1($('#j_password').val()).toString()
},
success: handleResponse,
error: function(response) {
reportError("invalid_user_pwd");
}
});
}


$.Class("pl.arrowgroup.DigestAuthentication", {
MAX_ATTEMPTS : 1,
AUTHORIZATION_HEADER : "Authorization",
Expand Down
32 changes: 32 additions & 0 deletions amp/docker/mvn-with-retry.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/sh
# Run Maven with retries to handle transient repository/network failures.

set -eu

if [ "$#" -eq 0 ]; then
echo "Usage: mvn-with-retry.sh <maven-args>" >&2
exit 1
fi

attempt=1
max_attempts="${MAVEN_MAX_ATTEMPTS:-4}"
base_delay_seconds="${MAVEN_RETRY_DELAY_SECONDS:-15}"

while [ "$attempt" -le "$max_attempts" ]; do
echo "Running Maven attempt ${attempt}/${max_attempts}: mvn $*"
if mvn "$@"; then
exit 0
fi

if [ "$attempt" -eq "$max_attempts" ]; then
break
fi

delay_seconds=$((base_delay_seconds * attempt))
echo "Maven command failed, retrying in ${delay_seconds}s..."
sleep "$delay_seconds"
attempt=$((attempt + 1))
done

echo "ERROR: Maven command failed after ${max_attempts} attempts" >&2
exit 1
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public AmpComponentField(String id, IModel<AmpActivityVersion> activityModel,
expeditures.setOutputMarkupPlaceholderTag(true);
add(expeditures);
} catch (Exception e) {
e.printStackTrace();
e.printStackTrace();
Comment thread
brianbrix marked this conversation as resolved.
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.digijava.kernel.ampapi.endpoints.security.AuthRule;
import org.digijava.kernel.ampapi.endpoints.util.ApiMethod;
import org.digijava.kernel.ampapi.endpoints.util.CalendarUtil;
import org.digijava.module.aim.dbentity.AmpFiscalCalendar;
Expand All @@ -22,7 +23,7 @@ public class CalendarEndpoint {

@GET
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
@ApiMethod(id = "getCalendar", ui = false)
@ApiMethod(id = "getCalendar", ui = false, authTypes = AuthRule.PUBLIC)
@ApiOperation("Retrieve calendars.")
public List<AmpFiscalCalendar> getCalendars(@QueryParam("id") List<Long> id) {
return CalendarUtil.getCalendars(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public class AmpConfiguration {
@GET
@Path("/settings")
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
@ApiMethod(ui = false, id = "Settings")
@ApiMethod(ui = false, id = "Settings", authTypes = AuthRule.PUBLIC)
@ApiOperation(
value = "Retrieve general AMP settings",
notes = "This endpoint provides access to general AMP configuration settings including " +
Expand All @@ -83,7 +83,7 @@ public Response getSettings() {
@GET
@Path("/settings/gis")
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
@ApiMethod(id = "GisSettings")
@ApiMethod(id = "GisSettings", authTypes = AuthRule.PUBLIC)
@ApiOperation("GIS settings")
@ApiResponses(@ApiResponse(code = HttpServletResponse.SC_OK, message = "GIS settings",
response = AmpGeneralSettings.class))
Expand All @@ -105,7 +105,7 @@ public Response describeTopsDashboard() {
@GET
@Path("/amp-offline-version-check")
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
@ApiMethod(ui = false, id = "version-check")
@ApiMethod(ui = false, id = "version-check", authTypes = AuthRule.PUBLIC)
@ApiOperation(
value = "Check if AMP Offline App is compatible with current AMP version",
notes = "This endpoint verifies compatibility between the AMP Offline application and the current AMP server version.\n\n" +
Expand Down Expand Up @@ -212,7 +212,7 @@ public Map<String, String> getGlobalSettings() {
@GET
@Path("global-settings/public")
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
@ApiMethod(ui = false, id = "public-global-settings")
@ApiMethod(ui = false, id = "public-global-settings", authTypes = AuthRule.PUBLIC)
@ApiOperation(
value = "Retrieve all public AMP Global Settings",
notes = "This endpoint provides access to public global configuration settings in the AMP system.\n\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class CommonEndpoint implements AmpEndpoint {
@POST
@Path("/fm")
@Produces(MediaType.APPLICATION_JSON)
@ApiMethod(ui = false, name = "fm", id = "")
@ApiMethod(ui = false, name = "fm", id = "", authTypes = AuthRule.PUBLIC)
@ApiOperation(value = "Provides FM (Feature Manager) settings for the requested options as a tree.")
public FMSettingsResult<FMSettingsTree> getFMSettings(
@ApiParam("FM Settings with requested options") FMSettingsConfig config) {
Expand All @@ -50,7 +50,7 @@ public FMSettingsResult<FMSettingsTree> getFMSettings(
@POST
@Path("/fm/flat")
@Produces(MediaType.APPLICATION_JSON)
@ApiMethod(ui = false, name = "fm", id = "")
@ApiMethod(ui = false, name = "fm", id = "", authTypes = AuthRule.PUBLIC)
@ApiOperation(value = "Provides FM (Feature Manager) settings for the requested options in flat mode.")
public FMSettingsResult<FMSettingsFlat> getFMSettingsFlat(
@ApiParam("FM Settings with requested options") FMSettingsConfig config) {
Expand All @@ -70,7 +70,7 @@ public Response describeGetFMSettingsFlat() {
@POST
@Path("/fm/flatAsResponse")
@Produces(MediaType.APPLICATION_JSON)
@ApiMethod(ui = false, name = "fm", id = "")
@ApiMethod(ui = false, name = "fm", id = "", authTypes = AuthRule.PUBLIC)
@ApiOperation(value = "Provides FM (Feature Manager) settings for the requested options in flat mode.")
public Response getFMSettingsFlatAsResponse(
@ApiParam("FM Settings with requested options") FMSettingsConfig config) {
Expand Down
Loading
Loading