1515import org .junit .jupiter .api .BeforeEach ;
1616import org .junit .jupiter .api .DisplayName ;
1717import org .junit .jupiter .api .Test ;
18+ import org .junit .jupiter .params .ParameterizedTest ;
19+ import org .junit .jupiter .params .provider .Arguments ;
20+ import org .junit .jupiter .params .provider .MethodSource ;
1821
1922import java .util .List ;
2023import java .util .concurrent .TimeUnit ;
24+ import java .util .function .BiConsumer ;
25+ import java .util .stream .Stream ;
2126
2227import static it .aboutbits .postgresql .core .KubernetesService .SECRET_DATA_BASIC_AUTH_PASSWORD_KEY ;
2328import static org .assertj .core .api .Assertions .assertThat ;
@@ -206,18 +211,22 @@ void nonSuperuserAdmin_updatesLoginAndMembership() {
206211 adminDsl .execute ("drop role if exists {0}" , role (parentRole ));
207212 }
208213
209- @ Test
210- @ DisplayName ("When the admin is not a superuser and the Role asks for SUPERUSER, the status should be ERROR" )
211- void nonSuperuserAdmin_superuserFlag_setsError () {
214+ @ ParameterizedTest (name = "flag {0}" )
215+ @ MethodSource ("provideSuperuserOnlyFlags" )
216+ @ DisplayName ("When the admin is not a superuser and the Role asks for a superuser-only flag, the status should be ERROR" )
217+ void nonSuperuserAdmin_superuserOnlyFlag_setsError (
218+ String flagName ,
219+ BiConsumer <RoleSpec .Flags , Boolean > flagSetter
220+ ) {
212221 // given
213- var rootConnection = givenRootClusterConnection ("test-connection-root-superuser -flag" );
222+ var rootConnection = givenRootClusterConnection ("test-connection-root-%s -flag" . formatted ( flagName ) );
214223 var rootDsl = postgreSQLContextFactory .getDSLContext (rootConnection );
215- var adminConnection = givenNonSuperuserClusterConnection (rootDsl , "test-connection-non-superuser-flag" );
224+ var adminConnection = givenNonSuperuserClusterConnection (rootDsl , "test-connection-non-superuser-%s- flag" . formatted ( flagName ) );
216225
217- var roleName = "test-non-superuser-role-superuser -flag" ;
226+ var roleName = "test-non-superuser-role-%s -flag" . formatted ( flagName ) ;
218227
219228 var flags = new RoleSpec .Flags ();
220- flags . setSuperuser ( true );
229+ flagSetter . accept ( flags , true );
221230
222231 // when
223232 var role = given .one ()
@@ -242,6 +251,84 @@ void nonSuperuserAdmin_superuserFlag_setsError() {
242251 assertThat (roleService .roleExists (rootDsl , role .getSpec ())).isFalse ();
243252 }
244253
254+ @ Test
255+ @ DisplayName ("When the admin is not a superuser and passwordEncryption is 'server', the server should hash the password" )
256+ void nonSuperuserAdmin_serverPasswordEncryption_letsTheServerHash () {
257+ // given
258+ var rootConnection = givenRootClusterConnection ("test-connection-root-server-encryption" );
259+ var rootDsl = postgreSQLContextFactory .getDSLContext (rootConnection );
260+ var adminConnection = givenNonSuperuserClusterConnection (rootDsl , "test-connection-non-superuser-server-encryption" );
261+
262+ var roleName = "test-non-superuser-role-server-encryption" ;
263+ var password = "server-side-password" ;
264+
265+ var secretRef = given .one ()
266+ .secretRef ()
267+ .withPassword (password )
268+ .returnFirst ();
269+
270+ // when
271+ var role = given .one ()
272+ .role ()
273+ .withName (roleName )
274+ .withClusterConnectionName (adminConnection .getMetadata ().getName ())
275+ .withPasswordSecretRef (secretRef )
276+ .withPasswordEncryption (PasswordEncryption .SERVER )
277+ .returnFirst ();
278+
279+ // then
280+ assertThat (role .getStatus ().getPhase ()).isEqualTo (CRPhase .READY );
281+ assertThat (PostgreSQLPasswordVerifier .passwordMatches (
282+ rootDsl ,
283+ roleName ,
284+ password
285+ )).isTrue ();
286+ }
287+
288+ @ Test
289+ @ DisplayName ("When the admin is not a superuser and the Secret contains a SCRAM-SHA-256 verifier, it should be stored verbatim" )
290+ void nonSuperuserAdmin_preHashedPassword_isStoredVerbatim () {
291+ // given
292+ var rootConnection = givenRootClusterConnection ("test-connection-root-prehashed" );
293+ var rootDsl = postgreSQLContextFactory .getDSLContext (rootConnection );
294+ var adminConnection = givenNonSuperuserClusterConnection (rootDsl , "test-connection-non-superuser-prehashed" );
295+
296+ var roleName = "test-non-superuser-role-prehashed" ;
297+
298+ // Verifier for the password "abc", generated by PostgreSQL
299+ var verifier = "SCRAM-SHA-256$4096:gxUQWxfrRYegSTNiHXFT+g==$lxMC2yO9Lx9gm2dgNPo/1Qar+pjAvxCP2VN4yPWYnzE=:0QzcS9VJHJszBq4vSce3n4M6NZmyWa1GWdkJDi8hRNc=" ;
300+
301+ var secretRef = given .one ()
302+ .secretRef ()
303+ .withPassword (verifier )
304+ .returnFirst ();
305+
306+ // when
307+ var role = given .one ()
308+ .role ()
309+ .withName (roleName )
310+ .withClusterConnectionName (adminConnection .getMetadata ().getName ())
311+ .withPasswordSecretRef (secretRef )
312+ .returnFirst ();
313+
314+ // then
315+ assertThat (role .getStatus ().getPhase ()).isEqualTo (CRPhase .READY );
316+ assertThat (PostgreSQLPasswordVerifier .storedVerifier (rootDsl , roleName )).isEqualTo (verifier );
317+ assertThat (PostgreSQLPasswordVerifier .passwordMatches (
318+ rootDsl ,
319+ roleName ,
320+ "abc"
321+ )).isTrue ();
322+ }
323+
324+ private static Stream <Arguments > provideSuperuserOnlyFlags () {
325+ return Stream .of (
326+ Arguments .of ("superuser" , (BiConsumer <RoleSpec .Flags , Boolean >) RoleSpec .Flags ::setSuperuser ),
327+ Arguments .of ("replication" , (BiConsumer <RoleSpec .Flags , Boolean >) RoleSpec .Flags ::setReplication ),
328+ Arguments .of ("bypassrls" , (BiConsumer <RoleSpec .Flags , Boolean >) RoleSpec .Flags ::setBypassrls )
329+ );
330+ }
331+
245332 private ClusterConnection givenRootClusterConnection (String name ) {
246333 return given .one ()
247334 .clusterConnection ()
0 commit comments