Skip to content

Commit c591125

Browse files
Merge branch 'main' into fix/SUP-148
2 parents c219c2a + bd6079e commit c591125

File tree

56 files changed

+2976
-2621
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2976
-2621
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ env:
1717
CARGO_TERM_COLOR: always
1818
CARGO_INCREMENTAL: '0'
1919
CARGO_PROFILE_DEV_DEBUG: '0'
20-
RUST_TOOLCHAIN_VERSION: "1.81.0"
20+
RUST_TOOLCHAIN_VERSION: "1.82.0"
2121
RUSTFLAGS: "-D warnings"
2222
RUSTDOCFLAGS: "-D warnings"
2323
RUST_LOG: "info"

.github/workflows/pr_pre-commit.yaml

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66

77
env:
88
CARGO_TERM_COLOR: always
9-
RUST_TOOLCHAIN_VERSION: "1.81.0"
9+
RUST_TOOLCHAIN_VERSION: "1.82.0"
1010
HADOLINT_VERSION: "v1.17.6"
1111

1212
jobs:
@@ -16,29 +16,10 @@ jobs:
1616
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
1717
with:
1818
fetch-depth: 0
19-
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
19+
- uses: stackabletech/actions/run-pre-commit@9bd13255f286e4b7a654617268abe1b2f37c3e0a # v0.3.0
2020
with:
21-
python-version: '3.12'
22-
- uses: dtolnay/rust-toolchain@master
23-
with:
24-
toolchain: ${{ env.RUST_TOOLCHAIN_VERSION }}
25-
components: rustfmt,clippy
26-
- name: Setup Hadolint
27-
shell: bash
28-
run: |
29-
set -euo pipefail
30-
31-
LOCATION_DIR="$HOME/.local/bin"
32-
LOCATION_BIN="$LOCATION_DIR/hadolint"
33-
34-
SYSTEM=$(uname -s)
35-
ARCH=$(uname -m)
36-
37-
mkdir -p "$LOCATION_DIR"
38-
curl -sL -o "${LOCATION_BIN}" "https://github.com/hadolint/hadolint/releases/download/${{ env.HADOLINT_VERSION }}/hadolint-$SYSTEM-$ARCH"
39-
chmod 700 "${LOCATION_BIN}"
40-
41-
echo "$LOCATION_DIR" >> "$GITHUB_PATH"
42-
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
43-
with:
44-
extra_args: "--from-ref ${{ github.event.pull_request.base.sha }} --to-ref ${{ github.event.pull_request.head.sha }}"
21+
rust: ${{ env.RUST_TOOLCHAIN_VERSION }}
22+
# rust-src is required for trybuild stderr output comparison to work
23+
# for our cases.
24+
# See: https://github.com/dtolnay/trybuild/issues/236#issuecomment-1620950759
25+
rust-components: rustfmt,clippy,rust-src

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/stackable-operator/CHANGELOG.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Fixed
8+
9+
- Fixed URL handling related to OIDC and `rootPath` with and without trailing slashes. Also added a bunch of tests ([#910]).
10+
11+
### Changed
12+
13+
- BREAKING: Made `DEFAULT_OIDC_WELLKNOWN_PATH` private. Use `AuthenticationProvider::well_known_config_url` instead ([#910]).
14+
15+
[#910]: https://github.com/stackabletech/operator-rs/pull/910
16+
717
## [0.81.0] - 2024-11-05
818

919
### Added
@@ -12,15 +22,15 @@ All notable changes to this project will be documented in this file.
1222

1323
### Changed
1424

15-
- BREAKING: Split `ListenerClass.spec.preferred_address_type` into a new `PreferredAddressType` type. Use `resolve_preferred_address_type()` to access the `AddressType` as before. ([#903])
25+
- BREAKING: Split `ListenerClass.spec.preferred_address_type` into a new `PreferredAddressType` type. Use `resolve_preferred_address_type()` to access the `AddressType` as before ([#903]).
1626
- BREAKING: Changed visibility of `commons::rbac::service_account_name` and `commons::rbac::role_binding_name` to
1727
private, as these functions should not be called directly by the operators. This is likely to result in naming conflicts
1828
as the result is completely dependent on what is passed to this function. Operators should instead rely on the roleBinding
1929
and serviceAccount objects created by `commons::rbac::build_rbac_resources` and retrieve the name from the returned
20-
objects if they need it. ([#909])
30+
objects if they need it ([#909]).
2131
- Changed the names of the objects that are returned from `commons::rbac::build_rbac_resources` to not rely solely on the product
2232
they refer to (e.g. "nifi-rolebinding") but instead include the name of the resource to be unique per cluster
23-
(e.g. simple-nifi-rolebinding). ([#909])
33+
(e.g. simple-nifi-rolebinding) ([#909]).
2434

2535
[#903]: https://github.com/stackabletech/operator-rs/pull/903
2636
[#909]: https://github.com/stackabletech/operator-rs/pull/909

crates/stackable-operator/src/commons/authentication/oidc.rs

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,19 @@ use crate::commons::{
1717

1818
pub type Result<T, E = Error> = std::result::Result<T, E>;
1919

20-
pub const DEFAULT_OIDC_WELLKNOWN_PATH: &str = ".well-known/openid-configuration";
2120
pub const CLIENT_ID_SECRET_KEY: &str = "clientId";
2221
pub const CLIENT_SECRET_SECRET_KEY: &str = "clientSecret";
2322

23+
/// Do *not* use this for [`Url::join`], as the leading slash will erase the existing path!
24+
const DEFAULT_WELLKNOWN_OIDC_CONFIG_PATH: &str = "/.well-known/openid-configuration";
25+
2426
#[derive(Debug, PartialEq, Snafu)]
2527
pub enum Error {
2628
#[snafu(display("failed to parse OIDC endpoint url"))]
2729
ParseOidcEndpointUrl { source: ParseError },
2830

2931
#[snafu(display(
30-
"failed to set OIDC endpoint scheme '{scheme}' for endpoint url '{endpoint}'"
32+
"failed to set OIDC endpoint scheme '{scheme}' for endpoint url \"{endpoint}\""
3133
))]
3234
SetOidcEndpointScheme { endpoint: Url, scheme: String },
3335
}
@@ -111,10 +113,10 @@ impl AuthenticationProvider {
111113
}
112114
}
113115

114-
/// Returns the OIDC endpoint [`Url`]. To append the default OIDC well-known
115-
/// configuration path, use `url.join()`. This module provides the default
116-
/// path at [`DEFAULT_OIDC_WELLKNOWN_PATH`].
117-
pub fn endpoint_url(&self) -> Result<Url> {
116+
/// Returns the OIDC base [`Url`] without any path segments.
117+
///
118+
/// The base url only contains the scheme, the host, and an optional port.
119+
fn base_url(&self) -> Result<Url> {
118120
let mut url = Url::parse(&format!(
119121
"http://{host}:{port}",
120122
host = self.hostname.as_url_host(),
@@ -132,7 +134,37 @@ impl AuthenticationProvider {
132134
})?;
133135
}
134136

135-
url.set_path(&self.root_path);
137+
Ok(url)
138+
}
139+
140+
/// Returns the OIDC endpoint [`Url`] without a trailing slash.
141+
///
142+
/// To retrieve the well-known OIDC configuration url, please use [`Self::well_known_config_url`].
143+
pub fn endpoint_url(&self) -> Result<Url> {
144+
let mut url = self.base_url()?;
145+
// Some tools can not cope with a trailing slash, so let's remove that
146+
url.set_path(self.root_path.trim_end_matches('/'));
147+
Ok(url)
148+
}
149+
150+
/// Returns the well-known OIDC configuration [`Url`] without a trailing slash.
151+
///
152+
/// The returned url is a combination of [`Self::endpoint_url`] joined with
153+
/// the well-known OIDC configuration path `DEFAULT_WELLKNOWN_OIDC_CONFIG_PATH`.
154+
pub fn well_known_config_url(&self) -> Result<Url> {
155+
let mut url = self.base_url()?;
156+
157+
// Taken from https://docs.rs/url/latest/url/struct.Url.html#method.join:
158+
// A trailing slash is significant. Without it, the last path component is considered to be
159+
// a “file” name to be removed to get at the “directory” that is used as the base.
160+
//
161+
// Because of that behavior, we first need to make sure that the root path doesn't contain
162+
// any trailing slashes to finally append the well-known config path to the url. The path
163+
// already contains a prefixed slash.
164+
let mut root_path_with_trailing_slash = self.root_path.trim_end_matches('/').to_string();
165+
root_path_with_trailing_slash.push_str(DEFAULT_WELLKNOWN_OIDC_CONFIG_PATH);
166+
url.set_path(&root_path_with_trailing_slash);
167+
136168
Ok(url)
137169
}
138170

@@ -246,6 +278,8 @@ pub struct ClientAuthenticationOptions<T = ()> {
246278

247279
#[cfg(test)]
248280
mod test {
281+
use rstest::rstest;
282+
249283
use super::*;
250284

251285
#[test]
@@ -310,12 +344,8 @@ mod test {
310344
.unwrap();
311345

312346
assert_eq!(
313-
oidc.endpoint_url()
314-
.unwrap()
315-
.join(DEFAULT_OIDC_WELLKNOWN_PATH)
316-
.unwrap()
317-
.as_str(),
318-
"https://my.keycloak.server/.well-known/openid-configuration"
347+
oidc.endpoint_url().unwrap().as_str(),
348+
"https://my.keycloak.server/"
319349
);
320350
}
321351

@@ -338,6 +368,70 @@ mod test {
338368
);
339369
}
340370

371+
#[rstest]
372+
#[case("/", "http://my.keycloak.server:1234/")]
373+
#[case("/realms/sdp", "http://my.keycloak.server:1234/realms/sdp")]
374+
#[case("/realms/sdp/", "http://my.keycloak.server:1234/realms/sdp")]
375+
#[case("/realms/sdp//////", "http://my.keycloak.server:1234/realms/sdp")]
376+
#[case(
377+
"/realms/my/realm/with/slashes//////",
378+
"http://my.keycloak.server:1234/realms/my/realm/with/slashes"
379+
)]
380+
fn root_path_endpoint_url(#[case] root_path: String, #[case] expected_endpoint_url: &str) {
381+
let oidc = serde_yaml::from_str::<AuthenticationProvider>(&format!(
382+
"
383+
hostname: my.keycloak.server
384+
port: 1234
385+
rootPath: {root_path}
386+
scopes: [openid]
387+
principalClaim: preferred_username
388+
"
389+
))
390+
.unwrap();
391+
392+
assert_eq!(oidc.endpoint_url().unwrap().as_str(), expected_endpoint_url);
393+
}
394+
395+
#[rstest]
396+
#[case("/", "https://my.keycloak.server/.well-known/openid-configuration")]
397+
#[case(
398+
"/realms/sdp",
399+
"https://my.keycloak.server/realms/sdp/.well-known/openid-configuration"
400+
)]
401+
#[case(
402+
"/realms/sdp/",
403+
"https://my.keycloak.server/realms/sdp/.well-known/openid-configuration"
404+
)]
405+
#[case(
406+
"/realms/sdp//////",
407+
"https://my.keycloak.server/realms/sdp/.well-known/openid-configuration"
408+
)]
409+
#[case(
410+
"/realms/my/realm/with/slashes//////",
411+
"https://my.keycloak.server/realms/my/realm/with/slashes/.well-known/openid-configuration"
412+
)]
413+
fn root_path_well_known_url(#[case] root_path: String, #[case] expected_well_known_url: &str) {
414+
let oidc = serde_yaml::from_str::<AuthenticationProvider>(&format!(
415+
"
416+
hostname: my.keycloak.server
417+
rootPath: {root_path}
418+
scopes: [openid]
419+
principalClaim: preferred_username
420+
tls:
421+
verification:
422+
server:
423+
caCert:
424+
webPki: {{}}
425+
"
426+
))
427+
.unwrap();
428+
429+
assert_eq!(
430+
oidc.well_known_config_url().unwrap().as_str(),
431+
expected_well_known_url
432+
);
433+
}
434+
341435
#[test]
342436
fn client_env_vars() {
343437
let secret_name = "my-keycloak-client";

crates/stackable-versioned-macros/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ itertools.workspace = true
3838
k8s-openapi = { workspace = true, optional = true }
3939
kube = { workspace = true, optional = true }
4040
proc-macro2.workspace = true
41-
strum.workspace = true
4241
syn.workspace = true
4342
quote.workspace = true
4443

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#[versioned(
2+
version(name = "v1alpha1"),
3+
version(name = "v1"),
4+
version(name = "v2alpha1")
5+
)]
6+
// ---
7+
pub(crate) mod versioned {
8+
pub struct Foo {
9+
bar: usize,
10+
11+
#[versioned(added(since = "v1"))]
12+
baz: bool,
13+
14+
#[versioned(deprecated(since = "v2alpha1"))]
15+
deprecated_foo: String,
16+
}
17+
18+
// The following attribute is just to ensure no strange behavior occurs.
19+
#[versioned]
20+
pub struct Bar {
21+
baz: String,
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#[versioned(
2+
version(name = "v1alpha1"),
3+
version(name = "v1"),
4+
version(name = "v2alpha1"),
5+
preserve_module
6+
)]
7+
// ---
8+
pub(crate) mod versioned {
9+
pub struct Foo {
10+
bar: usize,
11+
12+
#[versioned(added(since = "v1"))]
13+
baz: bool,
14+
15+
#[versioned(deprecated(since = "v2alpha1"))]
16+
deprecated_foo: String,
17+
}
18+
19+
#[versioned]
20+
pub struct Bar {
21+
baz: String,
22+
}
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#[versioned(
2+
version(name = "v1alpha1"),
3+
version(name = "v1beta1"),
4+
version(name = "v1"),
5+
k8s(
6+
group = "stackable.tech",
7+
singular = "foo",
8+
plural = "foos",
9+
namespaced,
10+
skip(merged_crd)
11+
)
12+
)]
13+
// ---
14+
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
15+
pub struct FooSpec {
16+
#[versioned(
17+
added(since = "v1beta1"),
18+
changed(since = "v1", from_name = "bah", from_type = "u16")
19+
)]
20+
bar: usize,
21+
baz: bool,
22+
}

crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@attribute_enum.rs.snap

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@attribute_struct.rs.snap

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@basic_struct.rs.snap

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)