Hello all,
For future reference, I actually made a mistake by trying to use
PBEWITHSHA256AND256BITAES-CBC to encrypt users passwords, as they don't
need to be decrypted (it's even better if they are not decryptable). I
should have used Geoserver's DIGEST, which is 16 bits salted sha256
iterated 100000 times, and which is straightforward to implement in Django.
That's what the complete Django password hasher looks like :
from django.contrib.auth.hashers import BasePasswordHasher, mask_hash
import hashlib, base64, os
from collections import OrderedDict
from django.utils.translation import gettext_noop as _t
from django.utils.crypto import constant_time_compare
class GeoserverDigestPasswordHasher(BasePasswordHasher):
"""
Geoserver Digest1 hashing
Iterations is hardcoded to 100000 (as this is what geoserver uses) and salt
is prepended to the hash
So Django's iteration and salt features don't make much sense
"""
algorithm = "geoserver_digest"
iterations = 100000
salt_size = 16
def salt(self):
return os.urandom(self.salt_size)
def encode(self, password, salt, iterations=None):
# Implementation translated from
https://github.com/jboss-fuse/jasypt/blob/jasypt-1_8/jasypt/src/main/java/org/jasypt/digest/StandardByteDigester.java#L943-L1009
assert password is not None
assert salt and '$' not in salt
if not iterations:
iterations = self.iterations
md = hashlib.sha256()
md.update(salt)
md.update(password)
digest = md.digest()
for i in range(iterations-1):
md = hashlib.sha256()
md.update( digest )
digest = md.digest()
return "%s$%s$-$%s" % (self.algorithm, iterations, base64.b64encode(salt +
digest))
def verify(self, password, encoded):
algorithm, iterations, _, hash = encoded.split('$', 3)
assert algorithm == self.algorithm
# The salt is hardcoded in the 16 first bytes, so we don't take it from the
value stored in algorithm$iterations$salt$hash
salt = base64.b64decode(hash)[0:16]
encoded_2 = self.encode(password, salt, int(iterations))
return constant_time_compare(encoded, encoded_2)
def safe_summary(self, encoded):
algorithm, iterations, _, hash = encoded.split('$', 3)
assert algorithm == self.algorithm
return OrderedDict([
(_t('algorithm'), algorithm),
(_t('iterations'), iterations),
(_t('hash'), mask_hash(hash)),
])
And that's the SQL view setup to have Django users in Geoserver (note you
can add Triggers to make the view updatable from withing Geoserver):
CREATE OR REPLACE FUNCTION dj2gs_hash(dj_hash text) RETURNS text LANGUAGE
plpgsql AS $$ BEGIN
RETURN CASE WHEN split_part(dj_hash,'$',4)='' THEN NULL
WHEN split_part(dj_hash,'$',1)='geoserver_digest' THEN 'digest1'
WHEN split_part(dj_hash,'$',1)='geoserver_plain' THEN 'plain'
ELSE split_part(dj_hash,'$',1)
END || ':' || split_part(dj_hash,'$',4);
END; $$;
CREATE OR REPLACE FUNCTION dj2gs_enabled(dj_enabled boolean) RETURNS text
LANGUAGE plpgsql AS $$ BEGIN
RETURN CASE
WHEN dj_enabled THEN 'Y'
ELSE 'N'
END;
END; $$;
CREATE OR REPLACE VIEW users AS
SELECT "username" as "name",
dj2gs_hash("password") as "password",
dj2gs_enabled("is_active") as "enabled"
FROM public.people_profile
WHERE NOT "username"='AnonymousUser';
So it all works :)
Kind regards,
Olivier
On Wed, Jan 24, 2018 at 5:25 PM, Olivier Dalang <[email protected]>
wrote:
> Hello Christian,
>
> Thanks for your suggestion. Unfortunately it doesn't work, as
> PBEWITHSHA256AND256BITAES-CBC is the only algorithm supported by this
> library.
>
> See the source:
>
> class StandardPBEStringEncryptor(object):
> def __init__(self, algorithm, salt_generator='Random', **kwargs):
> ...
> if algorithm == 'PBEWITHSHA256AND256BITAES-CBC':
> ....
> else:
> raise NotImplementedError('Algorithm %s is not implemented' %
> algorithm)
>
> Anyone one this ?
>
> Thanks !
>
> Olivier
>
>
>
> On Mon, Jan 22, 2018 at 5:07 PM, Christian Mueller <christian.mueller@os-
> solutions.at> wrote:
>
>> Hi Oliver
>>
>> try
>>
>> cryptor = StandardPBEStringEncryptor()
>>
>> instead of
>>
>> cryptor = StandardPBEStringEncryptor('PBEWITHSHA256AND256BITAES-CBC')
>>
>> On Sun, Jan 21, 2018 at 10:07 PM, Olivier Dalang <
>> [email protected]> wrote:
>>
>>> Dear List,
>>>
>>> (I hope this won't be posted twice, as I sent it a few days ago but it
>>> seems the mail didn't get through)
>>>
>>> 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.
>>>
>>>
>>> To give a bit of context, this is what I did so far) :
>>>
>>> 1/ Added a new JDBC "User Group Services" with database details in Geoserver
>>> and added it to the authentication provider chain
>>>
>>> 2/ Created a view in postgres like this (to replace the "users" table
>>> that Geoserver is expecting) :
>>> CREATE VIEW public.users AS
>>> SELECT "username" as "name",
>>> 'crypt2:' || split_part("password",'$',4) as "password", -- this
>>> extracts the hash from the django passwords
>>> CASE WHEN "is_active" THEN 'Y' ELSE 'N' END as enabled
>>> FROM public.people_profile;
>>>
>>> 3/ Created a custom password hasher for Django that using the jasypt4py
>>> python library (that supports "PBEWITHSHA256AND256BITAES-CBC")
>>> from jasypt4py import StandardPBEStringEncryptor
>>> class GeoserverLikePasswordHasher(BasePasswordHasher):
>>> algorithm = "geoserver-strong-pbe"
>>> iterations = 100000
>>>
>>> def encode(self, password, salt, iterations=None):
>>> cryptor = StandardPBEStringEncryptor('PBEWITHSHA256AND256BITAES-CBC')
>>> from_keystore = "4F24227A565B3C743F7D4A69504E2
>>> 7432C292D6E632C4A6B40384A4A60395F364743657969717076".decode("hex")
>>> hash = cryptor.encrypt( from_keystore, password, iterations)
>>> return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
>>>
>>> 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).
>>>
>>> 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.
>>>
>>> I'm new to Java and Geoserver, so I'm not really sure what's going on.
>>>
>>> I have two hypotheses :
>>>
>>> 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 ?
>>> 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...)
>>>
>>> Any idea of what to try next ? Has anyone been able to make such a setup
>>> work ?
>>>
>>> Thank you in advance for your help !!
>>>
>>> Olivier
>>>
>>>
>>>
>>>
>>> [1] This is the exception :
>>>
>>> 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)
>>>
>>>
>>>
>>> ------------------------------------------------------------
>>> ------------------
>>> Check out the vibrant tech community on one of the world's most
>>> engaging tech sites, Slashdot.org! http://sdm.link/slashdot
>>> _______________________________________________
>>> Geoserver-users mailing list
>>>
>>> Please make sure you read the following two resources before posting to
>>> this list:
>>> - Earning your support instead of buying it, but Ian Turton:
>>> http://www.ianturton.com/talks/foss4g.html#/
>>> - The GeoServer user list posting guidelines:
>>> http://geoserver.org/comm/userlist-guidelines.html
>>>
>>> If you want to request a feature or an improvement, also see this:
>>> https://github.com/geoserver/geoserver/wiki/Successfully-req
>>> uesting-and-integrating-new-features-and-improvements-in-GeoServer
>>>
>>>
>>> [email protected]
>>> https://lists.sourceforge.net/lists/listinfo/geoserver-users
>>>
>>>
>>
>>
>> --
>> DI Christian Mueller MSc (GIS), MSc (IT-Security)
>> OSS Open Source Solutions GmbH
>>
>>
>
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Geoserver-users mailing list
Please make sure you read the following two resources before posting to this
list:
- Earning your support instead of buying it, but Ian Turton:
http://www.ianturton.com/talks/foss4g.html#/
- The GeoServer user list posting guidelines:
http://geoserver.org/comm/userlist-guidelines.html
If you want to request a feature or an improvement, also see this:
https://github.com/geoserver/geoserver/wiki/Successfully-requesting-and-integrating-new-features-and-improvements-in-GeoServer
[email protected]
https://lists.sourceforge.net/lists/listinfo/geoserver-users