Skip to content

Commit

Permalink
Merge branch 'main' into jans-auth-server-10647
Browse files Browse the repository at this point in the history
  • Loading branch information
yuriyz authored Jan 16, 2025
2 parents e76990c + 5a53d53 commit 51c740a
Show file tree
Hide file tree
Showing 25 changed files with 748 additions and 397 deletions.
3 changes: 2 additions & 1 deletion docker-jans-auth-server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ RUN chmod -R g=u ${JETTY_BASE}/jans-auth/custom \
&& chown -R 1000:0 /opt/prometheus \
&& chown 1000:0 ${JETTY_BASE}/jans-auth/webapps/jans-auth.xml \
&& chown -R 1000:0 ${JETTY_HOME}/temp \
&& chown -R 1000:0 ${JETTY_BASE}/jans-auth/_libs
&& chown -R 1000:0 ${JETTY_BASE}/jans-auth/_libs \
&& chown -R 1000:0 /app/templates

USER 1000

Expand Down
3 changes: 2 additions & 1 deletion docker-jans-fido2/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ RUN chmod 664 ${JETTY_BASE}/jans-fido2/resources/log4j2.xml \
&& chown -R 1000:0 /usr/share/java \
&& chown -R 1000:0 /opt/prometheus \
&& chown 1000:0 ${JETTY_BASE}/jans-fido2/webapps/jans-fido2.xml \
&& chown -R 1000:0 ${JETTY_HOME}/temp
&& chown -R 1000:0 ${JETTY_HOME}/temp \
&& chown -R 1000:0 /app/templates

USER 1000

Expand Down
3 changes: 2 additions & 1 deletion docker-jans-kc-scheduler/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ RUN adduser -s /bin/sh -h /home/1000 -D -G root -u 1000 jans
RUN chmod -R g=u /etc/certs \
&& chmod -R g=u /etc/jans \
&& chmod 664 /opt/java/lib/security/cacerts \
&& chown -R 1000:0 /opt/kc-scheduler
&& chown -R 1000:0 /opt/kc-scheduler \
&& chown -R 1000:0 /app/templates

USER 1000

Expand Down
3 changes: 2 additions & 1 deletion docker-jans-keycloak-link/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@ RUN chmod 664 ${JETTY_BASE}/jans-keycloak-link/resources/log4j2.xml \
&& chown -R 1000:0 /opt/prometheus \
&& chown 1000:0 ${JETTY_BASE}/jans-keycloak-link/webapps/jans-keycloak-link.xml \
&& chown -R 1000:0 /var/jans/cr-snapshots \
&& chown -R 1000:0 ${JETTY_HOME}/temp
&& chown -R 1000:0 ${JETTY_HOME}/temp \
&& chown -R 1000:0 /app/templates

USER 1000

Expand Down
3 changes: 2 additions & 1 deletion docker-jans-link/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@ RUN chmod 664 ${JETTY_BASE}/jans-link/resources/log4j2.xml \
&& chown -R 1000:0 /opt/prometheus \
&& chown 1000:0 ${JETTY_BASE}/jans-link/webapps/jans-link.xml \
&& chown -R 1000:0 /var/jans/link-snapshots \
&& chown -R 1000:0 ${JETTY_HOME}/temp
&& chown -R 1000:0 ${JETTY_HOME}/temp \
&& chown -R 1000:0 /app/templates

USER 1000

Expand Down
3 changes: 2 additions & 1 deletion docker-jans-persistence-loader/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ RUN adduser -s /bin/sh -h /home/1000 -D -G root -u 1000 1000
# adjust ownership and permission
RUN chmod -R g=u /app/custom_ldif \
&& chmod -R g=u /etc/certs \
&& chmod -R g=u /etc/jans
&& chmod -R g=u /etc/jans \
&& chown -R 1000:0 /app/templates

USER 1000

Expand Down
3 changes: 2 additions & 1 deletion docker-jans-saml/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ RUN chmod -R g=u /etc/certs \
&& chown -R 1000:0 /opt/idp \
&& chown -R 1000:0 /usr/share/java \
&& chown -R 1000:0 /opt/keycloak/logs \
&& chown -R 1000:0 /opt/keycloak/conf
&& chown -R 1000:0 /opt/keycloak/conf \
&& chown -R 1000:0 /app/templates

USER 1000

Expand Down
3 changes: 2 additions & 1 deletion docker-jans-scim/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@ RUN chmod 664 ${JETTY_BASE}/jans-scim/resources/log4j2.xml \
&& chown -R 1000:0 /usr/share/java \
&& chown -R 1000:0 /opt/prometheus \
&& chown 1000:0 ${JETTY_BASE}/jans-scim/webapps/jans-scim.xml \
&& chown -R 1000:0 ${JETTY_HOME}/temp
&& chown -R 1000:0 ${JETTY_HOME}/temp \
&& chown -R 1000:0 /app/templates

USER 1000

Expand Down
27 changes: 24 additions & 3 deletions docs/script-catalog/authorization_challenge/AgamaChallenge.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.jans.agama.engine.client.MiniBrowser;
import io.jans.as.model.configuration.AppConfiguration;
import io.jans.as.model.util.Base64Util;
import io.jans.as.server.authorize.ws.rs.AuthzRequest;
import io.jans.util.*;

import jakarta.servlet.ServletRequest;
Expand Down Expand Up @@ -141,12 +142,14 @@ public boolean authorize(Object scriptContext) {

if (!CdiUtil.bean(FlowUtils.class).serviceEnabled())
return makeUnexpectedError(context, null, "Agama engine is disabled");

AuthzRequest authRequest = context.getAuthzRequest();

if (!context.getAuthzRequest().isUseAuthorizationChallengeSession())
if (!authRequest.isUseAuthorizationChallengeSession())
return makeMissingParamError(context, "Please set 'use_auth_session=true' in your request");

ServletRequest servletRequest = context.getHttpRequest();
AuthorizationChallengeSession deviceSessionObject = context.getAuthzRequest().getAuthorizationChallengeSessionObject();
AuthorizationChallengeSession deviceSessionObject = authRequest.getAuthorizationChallengeSessionObject();

boolean noSO = deviceSessionObject == null;
scriptLogger.debug("There IS{} device session object", noSO ? " NO" : "");
Expand Down Expand Up @@ -313,5 +316,23 @@ public int getApiVersion() {
public Map<String, String> getAuthenticationMethodClaims(Object context) {
return Map.of();
}


@Override
public void prepareAuthzRequest(Object scriptContext) {

ExternalScriptContext context = (ExternalScriptContext) scriptContext;
AuthzRequest authRequest = context.getAuthzRequest();

AuthorizationChallengeSession sessionObject = authRequest.getAuthorizationChallengeSessionObject();
if (sessionObject != null) {
Map<String, String> sessionAttributes = sessionObject.getAttributes().getAttributes();

// set scope from session into request object
String scopeFromSession = sessionAttributes.get("scope");
if (StringUtils.isNotBlank(scopeFromSession) && StringUtils.isBlank(authRequest.getScope())) {
authRequest.setScope(scopeFromSession);
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def destroy(self, configurationAttributes):
return True

def getApiVersion(self):
return 1
return 11

# Main consent-gather method. Must return True (if gathering performed successfully) or False (if fail).
# All user entered values can be access via Map<String, String> context.getPageAttributes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,20 @@ public String getClientDisplayName() {
}

final Client client = clientService.getClient(clientId);
return getCheckedClientDisplayName(client);
}

public String getClientDisplayName(final Client client) {
log.trace("client {}", client);

if (client == null) {
getClientDisplayName();
}

return getCheckedClientDisplayName(client);
}

private String getCheckedClientDisplayName(final Client client) {
if (StringUtils.isNotBlank(client.getClientName())) {
return client.getClientName();
}
Expand All @@ -998,7 +1012,7 @@ public String getClientDisplayName() {
}

return "Unknown";
}
}

public String getAuthReqId() {
return authReqId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,13 +442,14 @@ private Response processAuthorizationCode(String code, String scope, String code
executionContext.setGrant(authorizationCodeGrant);
log.trace("AuthorizationCodeGrant : '{}'", authorizationCodeGrant);

// if authorization code is not found then code was already used or wrong client provided = remove all grants with this auth code
tokenRestWebServiceValidator.validateGrant(authorizationCodeGrant, client, code, executionContext.getAuditLog(), grant -> grantService.removeAllByAuthorizationCode(code));

// validate redirectUri only for Authorization Code Flow. For First-Party App redirect uri is blank. It is perfectly valid case.
// redirect uri must be validated after grant is validated
if (!authorizationCodeGrant.isAuthorizationChallenge()) {
tokenRestWebServiceValidator.validateRedirectUri(redirectUri, executionContext.getAuditLog());
}

// if authorization code is not found then code was already used or wrong client provided = remove all grants with this auth code
tokenRestWebServiceValidator.validateGrant(authorizationCodeGrant, client, code, executionContext.getAuditLog(), grant -> grantService.removeAllByAuthorizationCode(code));
tokenRestWebServiceValidator.validatePKCE(authorizationCodeGrant, codeVerifier, executionContext.getAuditLog());
dPoPService.validateDpopThumprint(authorizationCodeGrant.getDpopJkt(), executionContext.getDpop());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
<h:outputFormat
value="#{msgs['authorize.requestingPermissionForScopes']}">
<f:param
value="#{authorizeAction.clientDisplayName}" />
value="#{authorizeAction.getClientDisplayName(client)}" />
</h:outputFormat>
</p>
</h3>
Expand Down
10 changes: 3 additions & 7 deletions jans-cedarling/cedarling/src/log/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub(crate) trait LogWriter {
pub(crate) trait Loggable: serde::Serialize {
/// get unique request ID
fn get_request_id(&self) -> Uuid;

/// get log level for entity
/// not all log entities have log level, only when `log_kind` == `System`
fn get_log_level(&self) -> Option<LogLevel>;
Expand All @@ -34,13 +35,8 @@ pub(crate) trait Loggable: serde::Serialize {
// is used to avoid boilerplate code
fn can_log(&self, logger_level: LogLevel) -> bool {
if let Some(entry_log_level) = self.get_log_level() {
if entry_log_level < logger_level {
// entry log level lower than logger level
false
} else {
// entry log higher or equal than logger level
true
}
// higher level is more important, ie closer to fatal
logger_level <= entry_log_level
} else {
// if `.get_log_level` return None
// it means that `log_kind` != `System` and we should log it
Expand Down
2 changes: 1 addition & 1 deletion jans-cedarling/cedarling/src/log/log_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ impl Loggable for &DecisionLogEntry<'_> {
// TODO: maybe using wasm we can use `js_sys::Date::now()`
// Static variable initialize only once at start of program and available during all program live cycle.
// Import inside function guarantee that it is used only inside function.
fn gen_uuid7() -> Uuid {
pub fn gen_uuid7() -> Uuid {
use std::sync::{LazyLock, Mutex};
use uuid7::V7Generator;

Expand Down
117 changes: 94 additions & 23 deletions jans-cedarling/cedarling/src/log/memory_logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ use super::interface::{LogStorage, LogWriter, Loggable};
use crate::bootstrap_config::log_config::MemoryLogConfig;

const STORAGE_MUTEX_EXPECT_MESSAGE: &str = "MemoryLogger storage mutex should unlock";
const STORAGE_JSON_PARSE_EXPECT_MESSAGE: &str =
"In MemoryLogger storage value should be valid LogEntry json string";

/// A logger that store logs in-memory.
pub(crate) struct MemoryLogger {
storage: Mutex<SparKV>,
storage: Mutex<SparKV<serde_json::Value>>,
log_level: LogLevel,
}

Expand All @@ -40,6 +38,44 @@ impl MemoryLogger {
}
}

/// In case of failure in MemoryLogger, log to stderr where supported.
/// On WASM, stderr is not supported, so log to whatever the wasm logger uses.
mod fallback {
use crate::LogLevel;

/// conform to Loggable requirement imposed by LogStrategy
#[derive(serde::Serialize)]
struct StrWrap<'a>(&'a str);

impl crate::log::interface::Loggable for StrWrap<'_> {
fn get_request_id(&self) -> uuid7::Uuid {
crate::log::log_entry::gen_uuid7()
}

fn get_log_level(&self) -> Option<LogLevel> {
// These must always be logged.
Some(LogLevel::TRACE)
}
}

/// Fetch the correct logger. That takes some work, and it's done on every
/// call. But this is a fallback logger, so it is not intended to be used
/// often, and in this case correctness and non-fallibility are far more
/// important than performance.
pub fn log(msg: &str) {
let log_config = crate::bootstrap_config::LogConfig{
log_type: crate::bootstrap_config::log_config::LogTypeConfig::StdOut,
// level is so that all messages passed here are logged.
log_level: LogLevel::TRACE,
};
// This should always be a LogStrategy::StdOut(StdOutLogger)
let log_strategy = crate::log::LogStrategy::new(&log_config);
use crate::log::interface::LogWriter;
// a string is always serializable
log_strategy.log_any(StrWrap(msg))
}
}

// Implementation of LogWriter
impl LogWriter for MemoryLogger {
fn log_any<T: Loggable>(&self, entry: T) {
Expand All @@ -48,43 +84,43 @@ impl LogWriter for MemoryLogger {
return;
}

let json_string = serde_json::json!(entry).to_string();
let json = match serde_json::to_value(&entry) {
Ok(json) => json,
Err(err) => {
fallback::log(&format!("could not serialize LogEntry to serde_json::Value: {err:?}"));
return;
},
};

let result = self
let set_result = self
.storage
.lock()
.expect(STORAGE_MUTEX_EXPECT_MESSAGE)
.set(entry.get_request_id().to_string().as_str(), &json_string);
.set(&entry.get_request_id().to_string(), json);

if let Err(err) = result {
// log error to stderr
eprintln!("could not store LogEntry to memory: {err:?}");
if let Err(err) = set_result {
fallback::log(&format!("could not store LogEntry to memory: {err:?}"));
};
}
}

// Implementation of LogStorage
impl LogStorage for MemoryLogger {
fn pop_logs(&self) -> Vec<serde_json::Value> {
// TODO: implement more efficient implementation

let mut storage_guard = self.storage.lock().expect(STORAGE_MUTEX_EXPECT_MESSAGE);

let keys = storage_guard.get_keys();

keys.iter()
.filter_map(|key| storage_guard.pop(key))
// we call unwrap, because we know that the value is valid json
.map(|str_json| serde_json::from_str::<serde_json::Value>(str_json.as_str())
.expect(STORAGE_JSON_PARSE_EXPECT_MESSAGE))
self.storage
.lock()
.expect(STORAGE_MUTEX_EXPECT_MESSAGE)
.drain()
.map(|(_k, value)| value)
.collect()
}

fn get_log_by_id(&self, id: &str) -> Option<serde_json::Value> {
self.storage.lock().expect(STORAGE_MUTEX_EXPECT_MESSAGE)
self.storage
.lock()
.expect(STORAGE_MUTEX_EXPECT_MESSAGE)
.get(id)
// we call unwrap, because we know that the value is valid json
.map(|str_json| serde_json::from_str::<serde_json::Value>(str_json.as_str()).expect(STORAGE_JSON_PARSE_EXPECT_MESSAGE))
.cloned()
}

fn get_log_ids(&self) -> Vec<String> {
Expand Down Expand Up @@ -211,4 +247,39 @@ mod tests {
"Logs were not fully popped"
);
}

#[test]
fn fallback_logger() {
struct FailSerialize;

impl serde::Serialize for FailSerialize {
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
Err(serde::ser::Error::custom("this always fails"))
}
}

impl crate::log::interface::Loggable for FailSerialize {
fn get_request_id(&self) -> uuid7::Uuid {
crate::log::log_entry::gen_uuid7()
}

fn get_log_level(&self) -> Option<LogLevel> {
// These must always be logged.
Some(LogLevel::TRACE)
}
}

let logger = create_memory_logger();
logger.log_any(FailSerialize);

// There isn't a good way, in unit tests, to verify the output was
// actually written to stderr/json console.
//
// To eyeball-verify it:
// cargo test -- --nocapture fall
// and look in the output for
// "could not serialize LogEntry to serde_json::Value: Error(\"this always fails\", line: 0, column: 0)"
assert!(logger.pop_logs().is_empty(), "logger should be empty");
}
}
3 changes: 3 additions & 0 deletions jans-cedarling/sparkv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ homepage = "https://crates.io/crates/sparkv"
[dependencies]
thiserror = { workspace = true }
chrono = { workspace = true }

[dev-dependencies]
serde_json = "*"
Loading

0 comments on commit 51c740a

Please sign in to comment.