Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@
.bundle/*
./local/*
.DS_Store
.env

/data/*
!/data/gingr
/data/gingr/ready/*
/data/gingr/processed/*
/data/gingr/failed/*
/data/gingr/processing/*

!/data/geoserver/
/data/geoserver/*
!/data/geoserver/keycloak/

/solr/geodata-test/data
/solr/geodata-test/data
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@ Command-line tool for ingesting GeoData (Solr, GeoServer, and file servers).
The app is Dockerized with an image designed to just run the `gingr` executable:

```sh
# set your env and then make any changes necessary.
cp .env.example .env

# Build the image
docker compose build

# The geoserver-config will need to run every time unless you mount the geoserver_data volume
# and comment out the geoserver-config service.
docker compose up -d

# Use `docker compose watch` to automatically rebuild on changes. This takes over the terminal
# you run it in, so you'll need to either background it (`screen` works great) or open a different window.
docker compose watch
Expand Down
92 changes: 92 additions & 0 deletions bin/geoserver-oidc-config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env sh
set -eu

GEOSERVER_AUTH="${GEOSERVER_AUTH:-admin:geoserver}"

# This script assumes both geoserver and geoserver-secure are healthy
# and configured exactly the same.
for GEOSERVER_HOST in geoserver geoserver-secure; do
GEOSERVER_REST="http://${GEOSERVER_HOST}:8080/geoserver/rest"
if [ "$GEOSERVER_HOST" = "geoserver-secure" ]; then
CLIENT_ID="${OIDC_SECURE_CLIENT_ID}"
CLIENT_SECRET="${OIDC_SECURE_CLIENT_SECRET}"
HOST_PORT="8081"
else
CLIENT_ID="${OIDC_CLIENT_ID}"
CLIENT_SECRET="${OIDC_CLIENT_SECRET}"
HOST_PORT="8080"
fi

echo "Creating keycloak OIDC auth filter on ${GEOSERVER_HOST}..."
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
--request POST \
--url "${GEOSERVER_REST}/security/authfilters" \
--user "${GEOSERVER_AUTH}" \
--header 'Content-Type: application/xml; charset=utf-8' \
Comment thread
anarchivist marked this conversation as resolved.
--data "<org.geoserver.security.oauth2.login.GeoServerOAuth2LoginFilterConfig>
<name>keycloak</name>
<className>org.geoserver.security.oauth2.login.GeoServerOAuth2LoginAuthenticationFilter</className>
<roleSource class=\"org.geoserver.security.oauth2.login.GeoServerOAuth2LoginFilterConfig\$OpenIdRoleSource\">IdToken</roleSource>
<baseRedirectUri>http://localhost:${HOST_PORT}/geoserver/</baseRedirectUri>
<googleEnabled>false</googleEnabled>
<googleUserNameAttribute>email</googleUserNameAttribute>
<googleRedirectUri>http://localhost:${HOST_PORT}/geoserver/web/login/oauth2/code/google</googleRedirectUri>
<gitHubEnabled>false</gitHubEnabled>
<gitHubUserNameAttribute>id</gitHubUserNameAttribute>
<gitHubRedirectUri>http://localhost:${HOST_PORT}/geoserver/web/login/oauth2/code/gitHub</gitHubRedirectUri>
<msEnabled>false</msEnabled>
<msUserNameAttribute>sub</msUserNameAttribute>
<msRedirectUri>http://localhost:${HOST_PORT}/geoserver/web/login/oauth2/code/microsoft</msRedirectUri>
<msScopes>openid profile email</msScopes>
<oidcEnabled>true</oidcEnabled>
<oidcClientId>${CLIENT_ID}</oidcClientId>
<oidcClientSecret>${CLIENT_SECRET}</oidcClientSecret>
<oidcUserNameAttribute>email</oidcUserNameAttribute>
<oidcRedirectUri>http://localhost:${HOST_PORT}/geoserver/web/login/oauth2/code/oidc</oidcRedirectUri>
<oidcScopes>openid berkeley_edu_groups email profile</oidcScopes>
<oidcDiscoveryUri>http://keycloak:8180/realms/berkeley-local/.well-known/openid-configuration</oidcDiscoveryUri>
<oidcTokenUri>http://keycloak:8180/realms/berkeley-local/protocol/openid-connect/token</oidcTokenUri>
<oidcAuthorizationUri>http://keycloak.localhost:8180/realms/berkeley-local/protocol/openid-connect/auth</oidcAuthorizationUri>
<oidcUserInfoUri>http://keycloak:8180/realms/berkeley-local/protocol/openid-connect/userinfo</oidcUserInfoUri>
<oidcJwkSetUri>http://keycloak:8180/realms/berkeley-local/protocol/openid-connect/certs</oidcJwkSetUri>
<oidcLogoutUri>http://keycloak.localhost:8180/realms/berkeley-local/protocol/openid-connect/logout</oidcLogoutUri>
<oidcForceAuthorizationUriHttps>false</oidcForceAuthorizationUriHttps>
<oidcForceTokenUriHttps>false</oidcForceTokenUriHttps>
<oidcEnforceTokenValidation>true</oidcEnforceTokenValidation>
<oidcUsePKCE>false</oidcUsePKCE>
<oidcAuthenticationMethodPostSecret>false</oidcAuthenticationMethodPostSecret>
<oidcAllowUnSecureLogging>false</oidcAllowUnSecureLogging>
<tokenRolesClaim>preferred_username</tokenRolesClaim>
<postLogoutRedirectUri>http://localhost:${HOST_PORT}/geoserver/web/</postLogoutRedirectUri>
<enableRedirectAuthenticationEntryPoint>true</enableRedirectAuthenticationEntryPoint>
<msGraphMemberOf>false</msGraphMemberOf>
<msGraphAppRoleAssignments>false</msGraphAppRoleAssignments>
<roleConverterString>testadmin=ADMIN</roleConverterString>
<onlyExternalListedRoles>false</onlyExternalListedRoles>
</org.geoserver.security.oauth2.login.GeoServerOAuth2LoginFilterConfig>")

case "$HTTP_STATUS" in
200|201) echo "Auth filter created." ;;
*) echo "ERROR: Unexpected status creating auth filter on ${GEOSERVER_HOST}: $HTTP_STATUS"; exit 1 ;;
esac

echo "Updating web filter chain on ${GEOSERVER_HOST}..."
FILTER_CHAIN_UPDATE_HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
--request PUT \
--url "${GEOSERVER_REST}/security/filterchain/web" \
--user "${GEOSERVER_AUTH}" \
--header 'Content-Type: application/xml; charset=utf-8' \
--data '<filters name="web" class="org.geoserver.security.HtmlLoginFilterChain" path="/web/**,/gwc/rest/web/**,/" disabled="false" allowSessionCreation="true" ssl="false" matchHTTPMethod="false" interceptorName="interceptor" exceptionTranslationName="exception">
<filter>rememberme</filter>
<filter>form</filter>
<filter>keycloak</filter>
<filter>anonymous</filter>
</filters>')

case "$FILTER_CHAIN_UPDATE_HTTP_STATUS" in
200) echo "Web filter chain updated." ;;
*) echo "ERROR: Unexpected status updating web filter chain: $FILTER_CHAIN_UPDATE_HTTP_STATUS"; exit 1 ;;
esac

echo "Done with ${GEOSERVER_HOST}."
done
123 changes: 123 additions & 0 deletions data/geoserver/keycloak/config/berkeley-local.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# keycloak-config-cli realm configuration for local GeoServer development.
# It provides a local Keycloak realm, GeoServer client, and Berkeley-style
# group claims for predictable OIDC testing.
#
# Group names mirror the production LDAP DN format so GeoServer can test the same
# role-mapping shape used against the real IdP.
#
# Secrets are injected at import time via substitution by keycloak-config-cli.
# Set OIDC_CLIENT_SECRET in your .env file — never commit a real secret here.

realm: berkeley-local
enabled: true
displayName: Berkeley Local

clientScopes:
- name: berkeley_edu_groups
description: Berkeley group memberships
protocol: openid-connect
attributes:
include.in.token.scope: "true"
display.on.consent.screen: "false"
protocolMappers:
- name: groups
protocol: openid-connect
protocolMapper: oidc-group-membership-mapper
consentRequired: false
config:
# Match Calnet's behavior by not prepending groups with "/"
full.path: "false"
id.token.claim: "true"
access.token.claim: "true"
introspection.token.claim: "true"
userinfo.token.claim: "true"
claim.name: groups
jsonType.label: String

clients:
Comment thread
anarchivist marked this conversation as resolved.
- clientId: "$(OIDC_CLIENT_ID)"
name: "$(OIDC_CLIENT_ID)"
enabled: true
rootUrl: "http://localhost:8080"
adminUrl: "http://localhost:8080"
redirectUris:
- "http://localhost:8080/*"
webOrigins:
- "http://localhost:8080"
publicClient: false
secret: "$(OIDC_CLIENT_SECRET)"
standardFlowEnabled: true
directAccessGrantsEnabled: true
serviceAccountsEnabled: true
frontchannelLogout: true
attributes:
backchannel.logout.session.required: "true"
frontchannel.logout.session.required: "true"
defaultClientScopes:
- profile
- email
- berkeley_edu_groups
- clientId: "$(OIDC_SECURE_CLIENT_ID)"
name: "$(OIDC_SECURE_CLIENT_ID)"
enabled: true
rootUrl: "http://localhost:8081"
adminUrl: "http://localhost:8081"
redirectUris:
- "http://localhost:8081/*"
webOrigins:
- "http://localhost:8081"
publicClient: false
secret: "$(OIDC_SECURE_CLIENT_SECRET)"
standardFlowEnabled: true
directAccessGrantsEnabled: true
serviceAccountsEnabled: true
frontchannelLogout: true
attributes:
backchannel.logout.session.required: "true"
frontchannel.logout.session.required: "true"
defaultClientScopes:
- profile
- email
- berkeley_edu_groups
groups:
- name: "$(OIDC_ADMIN_GROUP)"
- name: "$(OIDC_USER_GROUP)"
- name: "cn=edu:berkeley:org:libr:unrelated:group,ou=campus groups,dc=berkeley,dc=edu"

users:
- username: testadmin
enabled: true
email: testadmin@example.edu
firstName: Test
lastName: Admin
credentials:
- type: password
value: testadmin
temporary: false
groups:
- "$(OIDC_ADMIN_GROUP)"
- "$(OIDC_USER_GROUP)"

- username: testuser
enabled: true
email: testuser@example.edu
firstName: Test
lastName: User
credentials:
- type: password
value: testuser
temporary: false
groups:
- "$(OIDC_USER_GROUP)"

- username: testpublic
enabled: true
email: testpublic@example.edu
firstName: Test
lastName: Public
credentials:
- type: password
value: testpublic
temporary: false
groups:
- cn=edu:berkeley:org:libr:unrelated:group,ou=campus groups,dc=berkeley,dc=edu
8 changes: 8 additions & 0 deletions docker-compose.ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,13 @@ services:

geoserver-secure: !reset

geoserver-config: !reset

postgres: !reset

keycloak: !reset

keycloak-config: !reset

volumes:
artifacts:
Loading
Loading