Skip to content

Commit bb32582

Browse files
maltesanderrazvan
andcommitted
Consolidate top level config (#379)
# Description - reworked `clusterConfig` and `authentication` according to stackabletech/issues#293 fixes #339 Co-authored-by: Malte Sander <malte.sander.it@gmail.com> Co-authored-by: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com>
1 parent 3e6d1b9 commit bb32582

23 files changed

Lines changed: 514 additions & 407 deletions

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212

1313
### Changed
1414

15+
`operator-rs` `0.40.2` -> `0.42.2` ([#360], [#371]).
1516
- Use 0.0.0-dev product images for testing ([#361]).
1617
- Use testing-tools 0.2.0 ([#361]).
1718
- Added kuttl test suites ([#373]).
19+
- [BREAKING] Moved all top level config options to `clusterConfig`. Authentication is now provided via an array of AuthenticationClasses and additional properties ([#379]).
1820

1921
### Fixed
2022

@@ -29,6 +31,7 @@
2931
[#373]: https://github.com/stackabletech/superset-operator/pull/373
3032
[#375]: https://github.com/stackabletech/superset-operator/pull/375
3133
[#377]: https://github.com/stackabletech/superset-operator/pull/377
34+
[#379]: https://github.com/stackabletech/superset-operator/pull/379
3235

3336
## [23.4.0] - 2023-04-17
3437

deploy/helm/superset-operator/crds/crds.yaml

Lines changed: 139 additions & 130 deletions
Large diffs are not rendered by default.

docs/modules/superset/examples/getting_started/superset.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ spec:
88
productVersion: 2.1.0
99
stackableVersion: 0.0.0-dev
1010
clusterConfig:
11+
credentialsSecret: simple-superset-credentials
1112
listenerClass: external-unstable
12-
credentialsSecret: simple-superset-credentials
13-
loadExamplesOnInit: true
13+
loadExamplesOnInit: true
1414
nodes:
1515
roleGroups:
1616
default:

docs/modules/superset/examples/getting_started/superset.yaml.j2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ spec:
88
productVersion: 2.1.0
99
stackableVersion: {{ versions.superset }}
1010
clusterConfig:
11+
credentialsSecret: simple-superset-credentials
1112
listenerClass: external-unstable
12-
credentialsSecret: simple-superset-credentials
13-
loadExamplesOnInit: true
13+
loadExamplesOnInit: true
1414
nodes:
1515
roleGroups:
1616
default:

docs/modules/superset/pages/usage-guide/logging.adoc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ ConfigMap for the aggregator and by enabling the log agent:
66
[source,yaml]
77
----
88
spec:
9-
vectorAggregatorConfigMapName: vector-aggregator-discovery
9+
clusterConfig:
10+
databaseInitialization:
11+
logging:
12+
enableVectorAgent: true
13+
vectorAggregatorConfigMapName: vector-aggregator-discovery
1014
nodes:
1115
config:
1216
logging:
1317
enableVectorAgent: true
14-
databaseInitialization:
15-
logging:
16-
enableVectorAgent: true
1718
----
1819

1920
Further information on how to configure logging, can be found in

docs/modules/superset/pages/usage-guide/security.adoc

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ spec:
2121
image:
2222
productVersion: 2.1.0
2323
stackableVersion: 0.0.0-dev
24-
[...]
25-
authenticationConfig:
26-
authenticationClass: ldap # <1>
27-
userRegistrationRole: Admin # <2>
24+
clusterConfig:
25+
authentication:
26+
- authenticationClass: ldap # <1>
27+
userRegistrationRole: Admin # <2>
2828
----
2929

3030
<1> The reference to an AuthenticationClass called `ldap`
@@ -151,7 +151,7 @@ You can view all the available roles in the Webinterface of Superset and can als
151151

152152
=== LDAP
153153
Superset supports assigning https://superset.apache.org/docs/security/#roles[Roles] to users based on their LDAP group membership, though this is not yet supported by the Stackable operator.
154-
All the users logging in via LDAP get assigned to the same role which you can configure via the attribute `authenticationConfig.userRegistrationRole` on the `SupersetCluster` object:
154+
All the users logging in via LDAP get assigned to the same role which you can configure via the attribute `authentication[*].userRegistrationRole` on the `SupersetCluster` object:
155155

156156
[source,yaml]
157157
----
@@ -160,10 +160,10 @@ kind: SupersetCluster
160160
metadata:
161161
name: superset-with-ldap-server
162162
spec:
163-
[...]
164-
authenticationConfig:
165-
authenticationClass: ldap
166-
userRegistrationRole: Admin # <1>
163+
clusterConfig:
164+
authentication:
165+
- authenticationClass: ldap
166+
userRegistrationRole: Admin # <1>
167167
----
168168

169169
<1> All users are assigned to the `Admin` role

examples/superset-with-ldap.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,12 @@ spec:
152152
productVersion: 2.1.0
153153
stackableVersion: 0.0.0-dev
154154
clusterConfig:
155+
authentication:
156+
- authenticationClass: superset-with-ldap-server-veri-tls-ldap
157+
userRegistrationRole: Admin
158+
credentialsSecret: superset-with-ldap-server-veri-tls-credentials
155159
listenerClass: external-unstable
156-
credentialsSecret: superset-with-ldap-server-veri-tls-credentials
157160
nodes:
158161
roleGroups:
159162
default:
160-
config:
161-
authenticationConfig:
162-
authenticationClass: superset-with-ldap-server-veri-tls-ldap
163-
userRegistrationRole: Admin
163+
replicas: 1

rust/crd/src/authentication.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use serde::{Deserialize, Serialize};
2+
use snafu::{ResultExt, Snafu};
3+
use stackable_operator::commons::authentication::AuthenticationClassProvider;
4+
use stackable_operator::{
5+
client::Client,
6+
commons::authentication::AuthenticationClass,
7+
kube::runtime::reflector::ObjectRef,
8+
schemars::{self, JsonSchema},
9+
};
10+
11+
const SUPPORTED_AUTHENTICATION_CLASS_PROVIDERS: [&str; 1] = ["LDAP"];
12+
13+
#[derive(Snafu, Debug)]
14+
pub enum Error {
15+
#[snafu(display("Failed to retrieve AuthenticationClass {authentication_class}"))]
16+
AuthenticationClassRetrieval {
17+
source: stackable_operator::error::Error,
18+
authentication_class: ObjectRef<AuthenticationClass>,
19+
},
20+
// TODO: Adapt message if multiple authentication classes are supported simultaneously
21+
#[snafu(display("Only one authentication class is currently supported at a time"))]
22+
MultipleAuthenticationClassesProvided,
23+
#[snafu(display(
24+
"Failed to use authentication provider [{provider}] for authentication class [{authentication_class}] - supported providers: {SUPPORTED_AUTHENTICATION_CLASS_PROVIDERS:?}",
25+
))]
26+
AuthenticationProviderNotSupported {
27+
authentication_class: ObjectRef<AuthenticationClass>,
28+
provider: String,
29+
},
30+
}
31+
32+
type Result<T, E = Error> = std::result::Result<T, E>;
33+
34+
/// Resolved counter part for `SuperSetAuthenticationConfig`.
35+
pub struct SuperSetAuthenticationConfigResolved {
36+
pub authentication_class: Option<AuthenticationClass>,
37+
pub user_registration: bool,
38+
pub user_registration_role: String,
39+
pub sync_roles_at: FlaskRolesSyncMoment,
40+
}
41+
42+
#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
43+
#[serde(rename_all = "camelCase")]
44+
pub struct SupersetAuthentication {
45+
#[serde(default)]
46+
authentication: Vec<SuperSetAuthenticationConfig>,
47+
}
48+
49+
impl SupersetAuthentication {
50+
pub fn authentication_class_names(&self) -> Vec<&str> {
51+
let mut auth_classes = vec![];
52+
for config in &self.authentication {
53+
if let Some(auth_config) = &config.authentication_class {
54+
auth_classes.push(auth_config.as_str());
55+
}
56+
}
57+
auth_classes
58+
}
59+
}
60+
61+
#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
62+
#[serde(rename_all = "camelCase")]
63+
pub struct SuperSetAuthenticationConfig {
64+
/// Name of the AuthenticationClass used to authenticate the users.
65+
/// At the moment only LDAP is supported.
66+
/// If not specified the default authentication (AUTH_DB) will be used.
67+
pub authentication_class: Option<String>,
68+
/// Allow users who are not already in the FAB DB.
69+
/// Gets mapped to `AUTH_USER_REGISTRATION`
70+
#[serde(default = "default_user_registration")]
71+
pub user_registration: bool,
72+
/// This role will be given in addition to any AUTH_ROLES_MAPPING.
73+
/// Gets mapped to `AUTH_USER_REGISTRATION_ROLE`
74+
#[serde(default = "default_user_registration_role")]
75+
pub user_registration_role: String,
76+
/// If we should replace ALL the user's roles each login, or only on registration.
77+
/// Gets mapped to `AUTH_ROLES_SYNC_AT_LOGIN`
78+
#[serde(default = "default_sync_roles_at")]
79+
pub sync_roles_at: FlaskRolesSyncMoment,
80+
}
81+
82+
pub fn default_user_registration() -> bool {
83+
true
84+
}
85+
86+
pub fn default_user_registration_role() -> String {
87+
"Public".to_string()
88+
}
89+
90+
/// Matches Flask's default mode of syncing at registration
91+
pub fn default_sync_roles_at() -> FlaskRolesSyncMoment {
92+
FlaskRolesSyncMoment::Registration
93+
}
94+
95+
#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
96+
pub enum FlaskRolesSyncMoment {
97+
Registration,
98+
Login,
99+
}
100+
101+
impl SupersetAuthentication {
102+
/// Retrieve all provided `AuthenticationClass` references.
103+
pub async fn resolve(
104+
&self,
105+
client: &Client,
106+
) -> Result<Vec<SuperSetAuthenticationConfigResolved>> {
107+
let mut resolved = vec![];
108+
109+
// TODO: adapt if multiple authentication classes are supported by superset.
110+
// This is currently not possible due to the Flask App Builder not supporting it.
111+
if self.authentication.len() > 1 {
112+
return Err(Error::MultipleAuthenticationClassesProvided);
113+
}
114+
115+
for config in &self.authentication {
116+
let auth_class = if let Some(auth_class) = &config.authentication_class {
117+
let resolved = AuthenticationClass::resolve(client, auth_class)
118+
.await
119+
.context(AuthenticationClassRetrievalSnafu {
120+
authentication_class: ObjectRef::<AuthenticationClass>::new(auth_class),
121+
})?;
122+
123+
// Checking for supported AuthenticationClass here is a little out of place, but is does not
124+
// make sense to iterate further after finding an unsupported AuthenticationClass.
125+
Some(match resolved.spec.provider {
126+
AuthenticationClassProvider::Ldap(_) => resolved,
127+
AuthenticationClassProvider::Tls(_)
128+
| AuthenticationClassProvider::Static(_) => {
129+
return Err(Error::AuthenticationProviderNotSupported {
130+
authentication_class: ObjectRef::from_obj(&resolved),
131+
provider: resolved.spec.provider.to_string(),
132+
})
133+
}
134+
})
135+
} else {
136+
None
137+
};
138+
139+
resolved.push(SuperSetAuthenticationConfigResolved {
140+
authentication_class: auth_class,
141+
user_registration: config.user_registration,
142+
user_registration_role: config.user_registration_role.clone(),
143+
sync_roles_at: config.sync_roles_at.clone(),
144+
})
145+
}
146+
147+
Ok(resolved)
148+
}
149+
}

0 commit comments

Comments
 (0)