Skip to content

Commit

Permalink
Merge branch 'main' into jans-cedarling-10479
Browse files Browse the repository at this point in the history
  • Loading branch information
rmarinn authored Jan 15, 2025
2 parents d1df181 + 25c7a49 commit 7d82e44
Show file tree
Hide file tree
Showing 26 changed files with 724 additions and 401 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/build-nightly-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ jobs:
gh release delete ${NIGHTLY_VERSION} --cleanup-tag --yes || echo "v${NIGHTLY_VERSION}" does not exist
gh release delete ${NIGHTLY_VERSION} --cleanup-tag --yes || echo "v${NIGHTLY_VERSION}" does not exist
git push --delete origin ${NIGHTLY_VERSION} || echo "v${NIGHTLY_VERSION}" does not exist
gh release create ${NIGHTLY_VERSION} --generate-notes --prerelease --title "${NIGHTLY_VERSION}" --target ${{ github.event.inputs.branch }}
TARGET_BRANCH=${{ github.event.inputs.branch }}
if [ -z "$TARGET_BRANCH" ]; then
TARGET_BRANCH="main"
fi
gh release create ${NIGHTLY_VERSION} --generate-notes --prerelease --title "${NIGHTLY_VERSION}" --target "${TARGET_BRANCH}"
4 changes: 2 additions & 2 deletions .github/workflows/build-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ jobs:
cd ${{ github.workspace }}/jans-cedarling/target/wheels
sha256sum cedarling_python-"${TAG}"-cp311-cp311-manylinux_2_31_x86_64.whl > cedarling_python-"${TAG}"-cp311-cp311-manylinux_2_31_x86_64.whl.sha256sum
sha256sum cedarling_python-"${TAG}"-cp310-cp310-manylinux_2_31_x86_64.whl > cedarling_python-"${TAG}"-cp310-cp310-manylinux_2_31_x86_64.whl.sha256sum
gpg --armor --detach-sign cedarling_python-"${TAG}"-cp311-cp311-manylinux_2_34_x86_64.whl || echo "Failed to sign"
gpg --armor --detach-sign cedarling_python-"${TAG}"-cp310-cp310-manylinux_2_34_x86_64.whl || echo "Failed to sign"
gpg --armor --detach-sign cedarling_python-"${TAG}"-cp311-cp311-manylinux_2_31_x86_64.whl || echo "Failed to sign"
gpg --armor --detach-sign cedarling_python-"${TAG}"-cp310-cp310-manylinux_2_31_x86_64.whl || echo "Failed to sign"
echo "${{ secrets.MOAUTO_WORKFLOW_TOKEN }}" | gh auth login --with-token
gh release upload "${VERSION}" *.whl *.sha256sum *.asc
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
2 changes: 1 addition & 1 deletion docs/casa/developer/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ This is probably the most common requirement. Visit this [page](./add-authn-meth

### Other forms of customization

Most forms of customization can be tackled using flow cancellation. Through cancellation, a flow can be aborted while running and the control returned to one of its callers. Learn more about this topic [here](../../janssen-server/developer/agama/advanced-usages#cancellation).
Most forms of customization can be tackled using flow cancellation. Through cancellation, a flow can be aborted while running and the control returned to one of its callers. Learn more about this topic [here](../../janssen-server/developer/agama/advanced-usages.md#cancellation).

As an example, let's assume you want to add a _"don't have an account? register here"_ button in the initial screen of Casa flow. Here's what you can do:

Expand Down
14 changes: 8 additions & 6 deletions docs/janssen-server/auth-server/oauth-features/dpop.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,14 @@ recommended in the

Following properties of the Janssen Server can be used to tailor the behavior concerning DPoP.

- [dpopJtiCacheTime](https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpopjticachetime)
- [dpopSigningAlgValuesSupported](https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpopsigningalgvaluessupported)
- [dpopTimeframe](https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpoptimeframe)
- [dpopUseNonce](https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpopusenonce)
- [dpopNonceCacheTime](https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpopnoncecachetime)
- [dpopJktForceForAuthorizationCode]((https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpopjktforceforauthorizationcode))
- [dpopJtiCacheTime](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpopjticachetime)
- [dpopSigningAlgValuesSupported](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpopsigningalgvaluessupported)
- [dpopTimeframe](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpoptimeframe)
- [dpopUseNonce](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpopusenonce)
- [dpopNonceCacheTime](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpopnoncecachetime)
- [dpopJktForceForAuthorizationCode](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpopjktforceforauthorizationcode)



## Have questions in the meantime?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This document explains how to configure the Gluu Server so that when a user logs

## Prerequisites

- A Gluu Server (installation instructions [here](../../../../janssen-server/install/)) which will play the role of RADIUS client
- A Gluu Server (installation instructions [here](../../../../janssen-server/install/README.md)) which will play the role of RADIUS client
- The [Fortinet script](https://github.com/GluuFederation/oxAuth/blob/master/Server/integrations/fortinet/FortinetExternalAuthenticator.py) (included in the default Gluu Server distribution);
- A Fortinet server which is the RADIUS server.
- The jradius-client [jar library](https://sourceforge.net/projects/jradius-client/files/) added to oxAuth
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
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 = "*"
2 changes: 1 addition & 1 deletion jans-cedarling/sparkv/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ sparkv.set("your-key", "your-value"); // write
let value = sparkv.get("your-key").unwrap(); // read

// Write with unique TTL
sparkv.set_with_ttl("diff-ttl", "your-value", chrono::Duration::new(60, 0));
sparkv.set_with_ttl("diff-ttl", "your-value", chrono::Duration::seconds(60));
```

See `config.rs` for more configuration options.
Expand Down
14 changes: 4 additions & 10 deletions jans-cedarling/sparkv/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ impl Config {
Config {
max_items: 10_000,
max_item_size: 500_000,
max_ttl: Duration::new(60 * 60, 0).expect("a valid duration"),
default_ttl: Duration::new(5 * 60, 0).expect("a valid duration"), // 5 minutes
max_ttl: Duration::seconds(60 * 60),
default_ttl: Duration::seconds(5 * 60), // 5 minutes
auto_clear_expired: true,
}
}
Expand All @@ -43,14 +43,8 @@ mod tests {
let config: Config = Config::new();
assert_eq!(config.max_items, 10_000);
assert_eq!(config.max_item_size, 500_000);
assert_eq!(
config.max_ttl,
Duration::new(60 * 60, 0).expect("a valid duration")
);
assert_eq!(
config.default_ttl,
Duration::new(5 * 60, 0).expect("a valid duration")
);
assert_eq!(config.max_ttl, Duration::seconds(60 * 60));
assert_eq!(config.default_ttl, Duration::seconds(5 * 60));
assert!(config.auto_clear_expired);
}
}
Loading

0 comments on commit 7d82e44

Please sign in to comment.