<div dir="ltr"><div><div><div>Dear Lists,</div><div><br></div><div>I'm cross-posting to geoserver-users and geonode-users as this question concerns the setup of Geoserver but the end goal is to make it work with Geonode.</div><div><br></div><div>I'm trying to setup Geoserver to use a postgres user table from Django (the people_profile table from Geonode). I was able to make everything work except password encryption.</div><br><div></div><div><div><br></div><div>To give a bit of context, this is what I did so far) :</div><div><br></div><div><div>1/ Added a new JDBC "<span>User Group Services" with database details in </span>Geoserver and added it to the authentication provider chain</div><div><br></div><div>2/ Created a view in postgres like this (to replace the "users" table that Geoserver is expecting) :</div><div><div style="color:rgb(248,248,242);background-color:rgb(39,40,34);font-family:Consolas,"Courier New",monospace;font-weight:normal;font-size:14px;line-height:19px;white-space:pre-wrap"><div><span style="color:rgb(249,38,114)">CREATE</span><span style="color:rgb(248,248,242)"> VIEW </span><span style="color:rgb(174,129,255)">public</span><span style="color:rgb(248,248,242)">.</span><span style="color:rgb(174,129,255)">users</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(249,38,114)">AS</span></div><div><span style="color:rgb(249,38,114)">SELECT</span><span style="color:rgb(248,248,242)">  </span><span style="color:rgb(230,219,116)">"username"</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(249,38,114)">as</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(230,219,116)">"name"</span><span style="color:rgb(248,248,242)">,</span></div><div><span style="color:rgb(248,248,242)">        </span><span style="color:rgb(230,219,116)">'crypt2:'</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(249,38,114)">||</span><span style="color:rgb(248,248,242)"> split_part(</span><span style="color:rgb(230,219,116)">"password"</span><span style="color:rgb(248,248,242)">,</span><span style="color:rgb(230,219,116)">'$'</span><span style="color:rgb(248,248,242)">,</span><span style="color:rgb(174,129,255)">4</span><span style="color:rgb(248,248,242)">) </span><span style="color:rgb(249,38,114)">as</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(230,219,116)">"password"</span><span style="color:rgb(248,248,242)">, -- this extracts the hash from the django passwords</span></div><div><span style="color:rgb(248,248,242)">        </span><span style="color:rgb(249,38,114)">CASE</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(249,38,114)">WHEN</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(248,248,242)"></span><span style="color:rgb(230,219,116)">"is_active"</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(249,38,114)">THEN</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(230,219,116)">'Y'</span><span style="color:rgb(248,248,242)"> ELSE </span><span style="color:rgb(230,219,116)">'N'</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(249,38,114)">END</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(249,38,114)">as</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(249,38,114)">enabled</span></div><div><span style="color:rgb(249,38,114)">FROM</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(174,129,255)">public</span><span style="color:rgb(248,248,242)">.</span><span style="color:rgb(174,129,255)">people_profile</span><span style="color:rgb(248,248,242)">;<br></span></div></div></div><div><br></div><div>3/ Created a custom password hasher for Django that using the jasypt4py python library (that supports "PBEWITHSHA256AND256BITAES-CBC")<br></div><div><div style="color:rgb(248,248,242);background-color:rgb(39,40,34);font-family:Consolas,"Courier New",monospace;font-weight:normal;font-size:14px;line-height:19px;white-space:pre"><div><div style="color:rgb(248,248,242);background-color:rgb(39,40,34);font-family:Consolas,"Courier New",monospace;font-weight:normal;font-size:14px;line-height:19px;white-space:pre"><div><span style="color:rgb(249,38,114)">from</span><span style="color:rgb(248,248,242)"> jasypt4py </span><span style="color:rgb(249,38,114)">import</span><span style="color:rgb(248,248,242)"> StandardPBEStringEncryptor</span></div></div><span style="color:rgb(102,217,239);font-style:italic"></span></div><div><span style="color:rgb(102,217,239);font-style:italic">class</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(166,226,46)">GeoserverLikePasswordHasher</span><span style="color:rgb(248,248,242)">(</span><span style="color:rgb(166,226,46);font-style:italic">BasePasswordHasher</span><span style="color:rgb(248,248,242)">):</span></div><div><span style="color:rgb(248,248,242)">    algorithm </span><span style="color:rgb(249,38,114)">=</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(230,219,116)">"geoserver-strong-pbe"</span></div><div><span style="color:rgb(248,248,242)">    iterations </span><span style="color:rgb(249,38,114)">=</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(174,129,255)">100000</span></div><div><span style="color:rgb(174,129,255)"></span><br></div><div><span style="color:rgb(248,248,242)">    </span><span style="color:rgb(102,217,239);font-style:italic">def</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(166,226,46)">encode</span><span style="color:rgb(248,248,242)">(</span><span style="color:rgb(253,151,31);font-style:italic">self</span><span style="color:rgb(248,248,242)">, </span><span style="color:rgb(253,151,31);font-style:italic">password</span><span style="color:rgb(248,248,242)">, </span><span style="color:rgb(253,151,31);font-style:italic">salt</span><span style="color:rgb(248,248,242)">, </span><span style="color:rgb(253,151,31);font-style:italic">iterations</span><span style="color:rgb(249,38,114)">=</span><span style="color:rgb(174,129,255)">None</span><span style="color:rgb(248,248,242)">):</span></div><div style="color:rgb(248,248,242);background-color:rgb(39,40,34);font-family:Consolas,"Courier New",monospace;font-weight:normal;font-size:14px;line-height:19px;white-space:pre"><div><span style="color:rgb(248,248,242)">        cryptor </span><span style="color:rgb(249,38,114)">=</span><span style="color:rgb(248,248,242)"> StandardPBEStringEncryptor(</span><span style="color:rgb(230,219,116)">'PBEWITHSHA256AND256BITAES-CBC'</span><span style="color:rgb(248,248,242)">)</span></div><div><span style="color:rgb(248,248,242)">        from_keystore </span><span style="color:rgb(249,38,114)">=</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(230,219,116)">"4F24227A565B3C743F7D4A69504E27432C292D6E632C4A6B40384A4A60395F364743657969717076"</span><span style="color:rgb(248,248,242)">.decode(</span><span style="color:rgb(230,219,116)">"hex"</span><span style="color:rgb(248,248,242)">)</span></div><div><span style="color:rgb(248,248,242)">        </span><span style="color:rgb(102,217,239)">hash</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(249,38,114)">=</span><span style="color:rgb(248,248,242)"> cryptor.encrypt( from_keystore, password, iterations)</span></div></div><div><span style="color:rgb(248,248,242)">        </span><span style="color:rgb(249,38,114)">return</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(230,219,116)">"</span><span style="color:rgb(174,129,255)">%s</span><span style="color:rgb(230,219,116)">$</span><span style="color:rgb(174,129,255)">%d</span><span style="color:rgb(230,219,116)">$</span><span style="color:rgb(174,129,255)">%s</span><span style="color:rgb(230,219,116)">$</span><span style="color:rgb(174,129,255)">%s</span><span style="color:rgb(230,219,116)">"</span><span style="color:rgb(248,248,242)"> </span><span style="color:rgb(249,38,114)">%</span><span style="color:rgb(248,248,242)"> (</span><span style="color:rgb(253,151,31)">self</span><span style="color:rgb(248,248,242)">.algorithm, iterations, salt, </span><span style="color:rgb(102,217,239)">hash</span><span style="color:rgb(248,248,242)">)</span></div></div></div><div><br></div><div>Problem is, when I try to connect with these logins, I get an exception (see below[1]). The exception is not easy to debug as it's opaque on prupose (to avoid revealing sensitive details).<br></div><div><br></div><div>When I fake the encryption (by hardcoding a hash that was generated by Geoserver in the postgres view, or by using plain: and creating a PlainPasswordHasher), I can log in, which means that the problem really is at the encryption level.</div><div><br></div><div>I'm new to Java and Geoserver, so I'm not really sure what's going on.</div><div><br></div><div>I have two hypotheses :</div><div><br></div><div>1/ I'm using the wrong "password_key" (I'm using ug:django_key in my geoserver.jceks that was created when I configured the user group service. I also tried with config:password:key I could find there, but no more luck). Am I correct trying to use that one ?<br></div><div>2/ There's a problem with the jasypt4py library. In the readme, they say they only support  "PBEWITHSHA256AND256BITAES-CBC" from Jasypt/Bouncycastle. Is this what Geoserver uses ? Or are there several incompatible implementations of Jasypt ? (again, I'm new to Java...)</div><div><br></div>Any idea of what to try next ? Has anyone been able to make such a setup work ?</div><div><br></div><div>Thank you in advance for your help !!<br></div><div><br></div><div>Olivier</div><div><br></div><div><br></div><div><br></div><div><br></div><div>[1] This is the exception :<br></div><pre>org.jasypt.exceptions.EncryptionOperationNotPossibleException
        at org.jasypt.encryption.pbe.StandardPBEByteEncryptor.decrypt(StandardPBEByteEncryptor.java:981)
        at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.decrypt(StandardPBEStringEncryptor.java:717)
        at org.jasypt.spring.security3.PBEPasswordEncoder.isPasswordValid(PBEPasswordEncoder.java:218)
        at org.geoserver.security.password.AbstractGeoserverPasswordEncoder.isPasswordValid(AbstractGeoserverPasswordEncoder.java:142)
        at org.geoserver.security.password.GeoServerMultiplexingPasswordEncoder.isPasswordValid(GeoServerMultiplexingPasswordEncoder.java:91)
        at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:94)
        at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:165)
        at org.geoserver.security.auth.UsernamePasswordAuthenticationProvider.authenticate(UsernamePasswordAuthenticationProvider.java:82)
        at org.geoserver.security.GeoServerAuthenticationProvider.authenticate(GeoServerAuthenticationProvider.java:58)
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
        at org.geoserver.security.GeoServerSecurityManager$1.authenticate(GeoServerSecurityManager.java:323)
        at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:93)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
        at org.geoserver.security.filter.GeoServerCompositeFilter$NestedFilterChain.doFilter(GeoServerCompositeFilter.java:73)
        at org.geoserver.security.filter.GeoServerCompositeFilter.doFilter(GeoServerCompositeFilter.java:92)
        at org.geoserver.security.filter.GeoServerUserNamePasswordAuthenticationFilter.doFilter(GeoServerUserNamePasswordAuthenticationFilter.java:116)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.geoserver.security.filter.GeoServerCompositeFilter$NestedFilterChain.doFilter(GeoServerCompositeFilter.java:69)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
        at org.geoserver.security.filter.GeoServerSecurityContextPersistenceFilter$1.doFilter(GeoServerSecurityContextPersistenceFilter.java:53)
        at org.geoserver.security.filter.GeoServerCompositeFilter$NestedFilterChain.doFilter(GeoServerCompositeFilter.java:73)
        at org.geoserver.security.filter.GeoServerCompositeFilter.doFilter(GeoServerCompositeFilter.java:92)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
        at org.geoserver.security.GeoServerSecurityFilterChainProxy.doFilter(GeoServerSecurityFilterChainProxy.java:152)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
        at org.geoserver.filters.LoggingFilter.doFilter(LoggingFilter.java:88)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
        at org.geoserver.filters.XFrameOptionsFilter.doFilter(XFrameOptionsFilter.java:89)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
        at org.geoserver.filters.GZIPFilter.doFilter(GZIPFilter.java:42)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
        at org.geoserver.filters.SessionDebugFilter.doFilter(SessionDebugFilter.java:48)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
        at org.geoserver.filters.FlushSafeFilter.doFilter(FlushSafeFilter.java:44)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
        at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
        at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
        at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
        at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:215)
        at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:110)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
        at org.eclipse.jetty.server.Server.handle(Server.java:499)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:310)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
        at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:540)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
        at java.lang.Thread.run(Thread.java:745)</pre></div></div></div></div>