#
tokens: 49028/50000 7/140 files (page 4/7)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 4 of 7. Use http://codebase.md/chillbruhhh/crawl4ai-mcp?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .dockerignore
├── .env.example
├── .gitattributes
├── .gitignore
├── crawled_pages.sql
├── Dockerfile
├── knowledge_graphs
│   ├── ai_hallucination_detector.py
│   ├── ai_script_analyzer.py
│   ├── hallucination_reporter.py
│   ├── knowledge_graph_validator.py
│   ├── parse_repo_into_neo4j.py
│   ├── query_knowledge_graph.py
│   └── test_script.py
├── LICENSE
├── neo4j
│   └── docker-neo4j
│       ├── .github
│       │   └── ISSUE_TEMPLATE
│       │       └── bug_report.md
│       ├── .gitignore
│       ├── build-docker-image.sh
│       ├── build-utils-common-functions.sh
│       ├── COPYRIGHT
│       ├── DEVELOPMENT.md
│       ├── devenv
│       ├── devenv.local.template
│       ├── docker-image-src
│       │   ├── 2.3
│       │   │   ├── docker-entrypoint.sh
│       │   │   └── Dockerfile
│       │   ├── 3.0
│       │   │   ├── docker-entrypoint.sh
│       │   │   └── Dockerfile
│       │   ├── 3.1
│       │   │   ├── docker-entrypoint.sh
│       │   │   └── Dockerfile
│       │   ├── 3.2
│       │   │   ├── docker-entrypoint.sh
│       │   │   └── Dockerfile
│       │   ├── 3.3
│       │   │   ├── docker-entrypoint.sh
│       │   │   └── Dockerfile
│       │   ├── 3.4
│       │   │   ├── docker-entrypoint.sh
│       │   │   └── Dockerfile
│       │   ├── 3.5
│       │   │   ├── coredb
│       │   │   │   ├── docker-entrypoint.sh
│       │   │   │   ├── Dockerfile
│       │   │   │   └── neo4j-plugins.json
│       │   │   └── neo4j-admin
│       │   │       ├── docker-entrypoint.sh
│       │   │       └── Dockerfile
│       │   ├── 4.0
│       │   │   ├── coredb
│       │   │   │   ├── docker-entrypoint.sh
│       │   │   │   └── Dockerfile
│       │   │   └── neo4j-admin
│       │   │       ├── docker-entrypoint.sh
│       │   │       └── Dockerfile
│       │   ├── 4.1
│       │   │   ├── coredb
│       │   │   │   ├── docker-entrypoint.sh
│       │   │   │   └── Dockerfile
│       │   │   └── neo4j-admin
│       │   │       ├── docker-entrypoint.sh
│       │   │       └── Dockerfile
│       │   ├── 4.2
│       │   │   ├── coredb
│       │   │   │   ├── docker-entrypoint.sh
│       │   │   │   ├── Dockerfile
│       │   │   │   └── neo4j-plugins.json
│       │   │   └── neo4j-admin
│       │   │       ├── docker-entrypoint.sh
│       │   │       └── Dockerfile
│       │   ├── 4.3
│       │   │   ├── coredb
│       │   │   │   ├── docker-entrypoint.sh
│       │   │   │   ├── Dockerfile
│       │   │   │   └── neo4j-plugins.json
│       │   │   └── neo4j-admin
│       │   │       ├── docker-entrypoint.sh
│       │   │       └── Dockerfile
│       │   ├── 4.4
│       │   │   ├── coredb
│       │   │   │   ├── docker-entrypoint.sh
│       │   │   │   ├── Dockerfile-debian
│       │   │   │   ├── Dockerfile-ubi9
│       │   │   │   ├── neo4j-admin-report.sh
│       │   │   │   └── neo4j-plugins.json
│       │   │   └── neo4j-admin
│       │   │       ├── docker-entrypoint.sh
│       │   │       ├── Dockerfile-debian
│       │   │       └── Dockerfile-ubi9
│       │   ├── 5
│       │   │   ├── coredb
│       │   │   │   ├── docker-entrypoint.sh
│       │   │   │   ├── Dockerfile-debian
│       │   │   │   ├── Dockerfile-ubi8
│       │   │   │   ├── Dockerfile-ubi9
│       │   │   │   ├── neo4j-admin-report.sh
│       │   │   │   └── neo4j-plugins.json
│       │   │   └── neo4j-admin
│       │   │       ├── docker-entrypoint.sh
│       │   │       ├── Dockerfile-debian
│       │   │       ├── Dockerfile-ubi8
│       │   │       └── Dockerfile-ubi9
│       │   ├── calver
│       │   │   ├── coredb
│       │   │   │   ├── docker-entrypoint.sh
│       │   │   │   ├── Dockerfile-debian
│       │   │   │   ├── Dockerfile-ubi9
│       │   │   │   ├── neo4j-admin-report.sh
│       │   │   │   └── neo4j-plugins.json
│       │   │   └── neo4j-admin
│       │   │       ├── docker-entrypoint.sh
│       │   │       ├── Dockerfile-debian
│       │   │       └── Dockerfile-ubi9
│       │   └── common
│       │       ├── semver.jq
│       │       └── utilities.sh
│       ├── generate-stub-plugin
│       │   ├── build.gradle.kts
│       │   ├── Dockerfile
│       │   ├── ExampleNeo4jPlugin.java
│       │   ├── Makefile
│       │   ├── README.md
│       │   └── settings.gradle.kts
│       ├── LICENSE
│       ├── Makefile
│       ├── pom.xml
│       ├── publish-neo4j-admin-image.sh
│       ├── publish-neo4j-admin-images.sh
│       ├── README.md
│       └── src
│           ├── main
│           │   └── resources
│           │       └── log4j.properties
│           └── test
│               ├── java
│               │   └── com
│               │       └── neo4j
│               │           └── docker
│               │               ├── coredb
│               │               │   ├── configurations
│               │               │   │   ├── Configuration.java
│               │               │   │   ├── Setting.java
│               │               │   │   ├── TestConfSettings.java
│               │               │   │   ├── TestExtendedConf.java
│               │               │   │   └── TestJVMAdditionalConfig.java
│               │               │   ├── plugins
│               │               │   │   ├── Neo4jPluginEnv.java
│               │               │   │   ├── StubPluginHelper.java
│               │               │   │   ├── TestBundledPluginInstallation.java
│               │               │   │   ├── TestPluginInstallation.java
│               │               │   │   └── TestSemVerPluginMatching.java
│               │               │   ├── TestAdminReport.java
│               │               │   ├── TestAuthentication.java
│               │               │   ├── TestBasic.java
│               │               │   ├── TestCausalCluster.java
│               │               │   ├── TestMounting.java
│               │               │   └── TestUpgrade.java
│               │               ├── neo4jadmin
│               │               │   ├── TestAdminBasic.java
│               │               │   ├── TestBackupRestore.java
│               │               │   ├── TestBackupRestore44.java
│               │               │   ├── TestDumpLoad.java
│               │               │   ├── TestDumpLoad44.java
│               │               │   └── TestReport.java
│               │               ├── TestDeprecationWarning.java
│               │               ├── TestDockerComposeSecrets.java
│               │               └── utils
│               │                   ├── DatabaseIO.java
│               │                   ├── HostFileHttpHandler.java
│               │                   ├── HttpServerTestExtension.java
│               │                   ├── Neo4jVersion.java
│               │                   ├── Neo4jVersionTest.java
│               │                   ├── Network.java
│               │                   ├── SetContainerUser.java
│               │                   ├── TemporaryFolderManager.java
│               │                   ├── TemporaryFolderManagerTest.java
│               │                   ├── TestSettings.java
│               │                   └── WaitStrategies.java
│               └── resources
│                   ├── causal-cluster-compose.yml
│                   ├── confs
│                   │   ├── before50
│                   │   │   ├── ConfsNotOverridden.conf
│                   │   │   ├── ConfsReplaced.conf
│                   │   │   ├── EnterpriseOnlyNotOverwritten.conf
│                   │   │   ├── EnvVarsOverride.conf
│                   │   │   ├── ExtendedConf.conf
│                   │   │   ├── InvalidExtendedConf.conf
│                   │   │   ├── JvmAdditionalNotOverridden.conf
│                   │   │   ├── NoNewline.conf
│                   │   │   └── ReadConf.conf
│                   │   ├── ConfsNotOverridden.conf
│                   │   ├── ConfsReplaced.conf
│                   │   ├── EnterpriseOnlyNotOverwritten.conf
│                   │   ├── EnvVarsOverride.conf
│                   │   ├── ExtendedConf.conf
│                   │   ├── InvalidExtendedConf.conf
│                   │   ├── JvmAdditionalNotOverridden.conf
│                   │   ├── NoNewline.conf
│                   │   └── ReadConf.conf
│                   ├── dockersecrets
│                   │   ├── container-compose-with-incorrect-secrets.yml
│                   │   ├── container-compose-with-secrets-override.yml
│                   │   ├── container-compose-with-secrets.yml
│                   │   ├── simple-container-compose-with-external-file-var.yml
│                   │   └── simple-container-compose.yml
│                   ├── ha-cluster-compose.yml
│                   └── stubplugin
│                       └── myPlugin.jar
├── pyproject.toml
├── README.md
├── src
│   ├── crawl4ai_mcp.py
│   └── utils.py
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/3.4/docker-entrypoint.sh:
--------------------------------------------------------------------------------

```bash
  1 | #!/bin/bash -eu
  2 | 
  3 | cmd="$1"
  4 | 
  5 | function running_as_root
  6 | {
  7 |     test "$(id -u)" = "0"
  8 | }
  9 | 
 10 | function secure_mode_enabled
 11 | {
 12 |     test "${SECURE_FILE_PERMISSIONS:=no}" = "yes"
 13 | }
 14 | 
 15 | function containsElement
 16 | {
 17 |   local e match="$1"
 18 |   shift
 19 |   for e; do [[ "$e" == "$match" ]] && return 0; done
 20 |   return 1
 21 | }
 22 | 
 23 | function is_readable
 24 | {
 25 |     # this code is fairly ugly but works no matter who this script is running as.
 26 |     # It would be nice if the writability tests could use this logic somehow.
 27 |     local _file=${1}
 28 |     perm=$(stat -c %a "${_file}")
 29 | 
 30 |     # everyone permission
 31 |     if [[ ${perm:2:1} -ge 4 ]]; then
 32 |         return 0
 33 |     fi
 34 |     # owner permissions
 35 |     if [[ ${perm:0:1} -ge 4 ]]; then
 36 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 37 |             return 0
 38 |         fi
 39 |     fi
 40 |     # group permissions
 41 |     if [[ ${perm:1:1} -ge 4 ]]; then
 42 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 43 |             return 0
 44 |         fi
 45 |     fi
 46 |     return 1
 47 | }
 48 | 
 49 | function is_writable
 50 | {
 51 |     # It would be nice if this and the is_readable function could combine somehow
 52 |     local _file=${1}
 53 |     perm=$(stat -c %a "${_file}")
 54 | 
 55 |     # everyone permission
 56 |     if containsElement ${perm:2:1} 2 3 6 7; then
 57 |         return 0
 58 |     fi
 59 |     # owner permissions
 60 |     if containsElement ${perm:0:1} 2 3 6 7; then
 61 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 62 |             return 0
 63 |         fi
 64 |     fi
 65 |     # group permissions
 66 |     if containsElement ${perm:1:1} 2 3 6 7; then
 67 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 68 |             return 0
 69 |         fi
 70 |     fi
 71 |     return 1
 72 | }
 73 | 
 74 | 
 75 | function print_permissions_advice_and_fail
 76 | {
 77 |     _directory=${1}
 78 |     echo >&2 "
 79 | Folder ${_directory} is not accessible for user: ${userid} or group ${groupid} or groups ${groups[@]}, this is commonly a file permissions issue on the mounted folder.
 80 | 
 81 | Hints to solve the issue:
 82 | 1) Make sure the folder exists before mounting it. Docker will create the folder using root permissions before starting the Neo4j container. The root permissions disallow Neo4j from writing to the mounted folder.
 83 | 2) Pass the folder owner's user ID and group ID to docker run, so that docker runs as that user.
 84 | If the folder is owned by the current user, this can be done by adding this flag to your docker run command:
 85 |   --user=\$(id -u):\$(id -g)
 86 |        "
 87 |     exit 1
 88 | }
 89 | 
 90 | function check_mounted_folder_readable
 91 | {
 92 |     local _directory=${1}
 93 |     if ! is_readable "${_directory}"; then
 94 |         print_permissions_advice_and_fail "${_directory}"
 95 |     fi
 96 | }
 97 | 
 98 | function check_mounted_folder_with_chown
 99 | {
100 | # The /data and /log directory are a bit different because they are very likely to be mounted by the user but not
101 | # necessarily writable.
102 | # This depends on whether a user ID is passed to the container and which folders are mounted.
103 | #
104 | #   No user ID passed to container:
105 | #   1) No folders are mounted.
106 | #      The /data and /log folder are owned by neo4j by default, so should be writable already.
107 | #   2) Both /log and /data are mounted.
108 | #      This means on start up, /data and /logs are owned by an unknown user and we should chown them to neo4j for
109 | #      backwards compatibility.
110 | #
111 | #   User ID passed to container:
112 | #   1) Both /data and /logs are mounted
113 | #      The /data and /logs folders are owned by an unknown user but we *should* have rw permission to them.
114 | #      That should be verified and error (helpfully) if not.
115 | #   2) User mounts /data or /logs *but not both*
116 | #      The  unmounted folder is still owned by neo4j, which should already be writable. The mounted folder should
117 | #      have rw permissions through user id. This should be verified.
118 | #   3) No folders are mounted.
119 | #      The /data and /log folder are owned by neo4j by default, and these are already writable by the user.
120 | #      (This is a very unlikely use case).
121 | 
122 |     local mountFolder=${1}
123 |     if running_as_root; then
124 |         if ! is_writable "${mountFolder}" && ! secure_mode_enabled; then
125 |             # warn that we're about to chown the folder and then chown it
126 |             echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
127 |             chown -R "${userid}":"${groupid}" "${mountFolder}"
128 |         fi
129 |     else
130 |         if [[ ! -w "${mountFolder}" ]]  && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
131 |             print_permissions_advice_and_fail "${mountFolder}"
132 |         fi
133 |     fi
134 | }
135 | 
136 | function load_plugin_from_github
137 | {
138 |   # Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
139 |   # correct format.
140 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
141 | 
142 |   local _plugins_dir="${NEO4J_HOME}/plugins"
143 |   if [ -d /plugins ]; then
144 |     local _plugins_dir="/plugins"
145 |   fi
146 |   local _versions_json_url="$(jq --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.versions" /neo4j-plugins.json )"
147 |   # Using the same name for the plugin irrespective of version ensures we don't end up with different versions of the same plugin
148 |   local _destination="${_plugins_dir}/${_plugin_name}.jar"
149 |   local _neo4j_version="$(neo4j --version | cut -d' ' -f2)"
150 | 
151 |   # Now we call out to github to get the versions.json for this plugin and we parse that to find the url for the correct plugin jar for our neo4j version
152 |   echo "Fetching versions.json for Plugin '${_plugin_name}' from ${_versions_json_url}"
153 |   local _versions_json="$(wget -q --timeout 300 --tries 30 -O - "${_versions_json_url}")"
154 |   local _plugin_jar_url="$(echo "${_versions_json}" | jq --raw-output ".[] | select(.neo4j==\"${_neo4j_version}\") | .jar")"
155 |   if [[ -z "${_plugin_jar_url}" ]]; then
156 |     echo >&2 "Error: No jar URL found for version '${_neo4j_version}' in versions.json from '${_versions_json_url}'"
157 |     echo >&2 "${_versions_json}"
158 |     exit 1
159 |   fi
160 |   echo "Installing Plugin '${_plugin_name}' from ${_plugin_jar_url} to ${_destination} "
161 |   wget -q --timeout 300 --tries 30 --output-document="${_destination}" "${_plugin_jar_url}"
162 | 
163 |   if ! is_readable "${_destination}"; then
164 |     echo >&2 "Plugin at '${_destination}' is not readable"
165 |     exit 1
166 |   fi
167 | }
168 | 
169 | function apply_plugin_default_configuration
170 | {
171 |   # Set the correct Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
172 |   # correct format.
173 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
174 |   local _reference_conf="${2}" # used to determine if we can override properties
175 |   local _neo4j_conf="${NEO4J_HOME}/conf/neo4j.conf"
176 | 
177 |   local _property _value
178 |   echo "Applying default values for plugin ${_plugin_name} to neo4j.conf"
179 |   for _entry in $(jq  --compact-output --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.properties | to_entries[]" /neo4j-plugins.json); do
180 |     _property="$(jq --raw-output '.key' <<< "${_entry}")"
181 |     _value="$(jq --raw-output '.value' <<< "${_entry}")"
182 | 
183 |     # the first grep strips out comments
184 |     if grep -o "^[^#]*" "${_reference_conf}" | grep -q --fixed-strings "${_property}=" ; then
185 |       # property is already set in the user provided config. In this case we don't override what has been set explicitly by the user.
186 |       echo "Skipping ${_property} for plugin ${_plugin_name} because it is already set"
187 |     else
188 |       if grep -o "^[^#]*" "${_neo4j_conf}" | grep -q --fixed-strings "${_property}=" ; then
189 |         sed --in-place "s/${_property}=/&${_value},/" "${_neo4j_conf}"
190 |       else
191 |         echo "${_property}=${_value}" >> "${_neo4j_conf}"
192 |       fi
193 |     fi
194 |   done
195 | }
196 | 
197 | function install_neo4j_labs_plugins
198 | {
199 |   # We store a copy of the config before we modify it for the plugins to allow us to see if there are user-set values in the input config that we shouldn't override
200 |   local _old_config="$(mktemp)"
201 |   cp "${NEO4J_HOME}"/conf/neo4j.conf "${_old_config}"
202 |   for plugin_name in $(echo "${NEO4JLABS_PLUGINS}" | jq --raw-output '.[]'); do
203 |     load_plugin_from_github "${plugin_name}"
204 |     apply_plugin_default_configuration "${plugin_name}" "${_old_config}"
205 |   done
206 |   rm "${_old_config}"
207 | }
208 | 
209 | # If we're running as root, then run as the neo4j user. Otherwise
210 | # docker is running with --user and we simply use that user.  Note
211 | # that su-exec, despite its name, does not replicate the functionality
212 | # of exec, so we need to use both
213 | if running_as_root; then
214 |   userid="neo4j"
215 |   groupid="neo4j"
216 |   groups=($(id -G neo4j))
217 |   exec_cmd="exec gosu neo4j:neo4j"
218 | else
219 |   userid="$(id -u)"
220 |   groupid="$(id -g)"
221 |   groups=($(id -G))
222 |   exec_cmd="exec"
223 | fi
224 | readonly userid
225 | readonly groupid
226 | readonly groups
227 | readonly exec_cmd
228 | 
229 | 
230 | # Need to chown the home directory - but a user might have mounted a
231 | # volume here (notably a conf volume). So take care not to chown
232 | # volumes (stuff not owned by neo4j)
233 | if running_as_root; then
234 |     # Non-recursive chown for the base directory
235 |     chown "${userid}":"${groupid}" "${NEO4J_HOME}"
236 |     chmod 700 "${NEO4J_HOME}"
237 |     find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -type d -exec chown -R ${userid}:${groupid} {} \;
238 |     find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -type d -exec chmod -R 700 {} \;
239 | fi
240 | 
241 | # Only prompt for license agreement if command contains "neo4j" in it
242 | if [[ "${cmd}" == *"neo4j"* ]]; then
243 |   if [ "${NEO4J_EDITION}" == "enterprise" ]; then
244 |     if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then
245 |       echo >&2 "
246 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
247 | 
248 | (c) Neo4j Sweden AB. 2021.  All Rights Reserved.
249 | Use of this Software without a proper commercial license with Neo4j,
250 | Inc. or its affiliates is prohibited.
251 | 
252 | Email inquiries can be directed to: [email protected]
253 | 
254 | More information is also available at: https://neo4j.com/licensing/
255 | 
256 | 
257 | To accept the license agreement set the environment variable
258 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
259 | 
260 | To do this you can use the following docker argument:
261 | 
262 |         --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
263 | "
264 |       exit 1
265 |     fi
266 |   fi
267 | fi
268 | 
269 | # Env variable naming convention:
270 | # - prefix NEO4J_
271 | # - double underscore char '__' instead of single underscore '_' char in the setting name
272 | # - underscore char '_' instead of dot '.' char in the setting name
273 | # Example:
274 | # NEO4J_dbms_tx__log_rotation_retention__policy env variable to set
275 | #       dbms.tx_log.rotation.retention_policy setting
276 | 
277 | # Backward compatibility - map old hardcoded env variables into new naming convention (if they aren't set already)
278 | # Set some to default values if unset
279 | : ${NEO4J_dbms_tx__log_rotation_retention__policy:=${NEO4J_dbms_txLog_rotation_retentionPolicy:-"100M size"}}
280 | : ${NEO4J_wrapper_java_additional:=${NEO4J_UDC_SOURCE:-"-Dneo4j.ext.udc.source=docker"}}
281 | : ${NEO4J_dbms_unmanaged__extension__classes:=${NEO4J_dbms_unmanagedExtensionClasses:-}}
282 | : ${NEO4J_dbms_allow__format__migration:=${NEO4J_dbms_allowFormatMigration:-}}
283 | : ${NEO4J_dbms_connectors_default__advertised__address:=${NEO4J_dbms_connectors_defaultAdvertisedAddress:-}}
284 | : ${NEO4J_ha_server__id:=${NEO4J_ha_serverId:-}}
285 | : ${NEO4J_ha_initial__hosts:=${NEO4J_ha_initialHosts:-}}
286 | 
287 | if [ "${NEO4J_EDITION}" == "enterprise" ];
288 |   then
289 |    : ${NEO4J_causal__clustering_expected__core__cluster__size:=${NEO4J_causalClustering_expectedCoreClusterSize:-}}
290 |    : ${NEO4J_causal__clustering_initial__discovery__members:=${NEO4J_causalClustering_initialDiscoveryMembers:-}}
291 |    : ${NEO4J_causal__clustering_discovery__advertised__address:=${NEO4J_causalClustering_discoveryAdvertisedAddress:-"$(hostname):5000"}}
292 |    : ${NEO4J_causal__clustering_transaction__advertised__address:=${NEO4J_causalClustering_transactionAdvertisedAddress:-"$(hostname):6000"}}
293 |    : ${NEO4J_causal__clustering_raft__advertised__address:=${NEO4J_causalClustering_raftAdvertisedAddress:-"$(hostname):7000"}}
294 |    # Custom settings for dockerized neo4j
295 |    : ${NEO4J_ha_host_coordination:=$(hostname):5001}
296 |    : ${NEO4J_ha_host_data:=$(hostname):6001}
297 |    : ${NEO4J_causal__clustering_discovery__advertised__address:=$(hostname):5000}
298 |    : ${NEO4J_causal__clustering_transaction__advertised__address:=$(hostname):6000}
299 |    : ${NEO4J_causal__clustering_raft__advertised__address:=$(hostname):7000}
300 | fi
301 | 
302 | # unset old hardcoded unsupported env variables
303 | unset NEO4J_dbms_txLog_rotation_retentionPolicy NEO4J_UDC_SOURCE \
304 |     NEO4J_dbms_unmanagedExtensionClasses NEO4J_dbms_allowFormatMigration \
305 |     NEO4J_dbms_connectors_defaultAdvertisedAddress NEO4J_ha_serverId \
306 |     NEO4J_ha_initialHosts NEO4J_causalClustering_expectedCoreClusterSize \
307 |     NEO4J_causalClustering_initialDiscoveryMembers \
308 |     NEO4J_causalClustering_discoveryListenAddress \
309 |     NEO4J_causalClustering_discoveryAdvertisedAddress \
310 |     NEO4J_causalClustering_transactionListenAddress \
311 |     NEO4J_causalClustering_transactionAdvertisedAddress \
312 |     NEO4J_causalClustering_raftListenAddress \
313 |     NEO4J_causalClustering_raftAdvertisedAddress
314 | 
315 | if [ -d /conf ]; then
316 |     if secure_mode_enabled; then
317 | 	    check_mounted_folder_readable "/conf"
318 |     fi
319 |     find /conf -type f -exec cp {} "${NEO4J_HOME}"/conf \;
320 | fi
321 | 
322 | if [ -d /ssl ]; then
323 |     if secure_mode_enabled; then
324 |     	check_mounted_folder_readable "/ssl"
325 |     fi
326 |     : ${NEO4J_dbms_directories_certificates:="/ssl"}
327 | fi
328 | 
329 | if [ -d /plugins ]; then
330 |     if secure_mode_enabled; then
331 |         if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then
332 |             # We need write permissions
333 |             check_mounted_folder_with_chown "/plugins"
334 |         fi
335 |         check_mounted_folder_readable "/plugins"
336 |     fi
337 |     : ${NEO4J_dbms_directories_plugins:="/plugins"}
338 | fi
339 | 
340 | if [ -d /import ]; then
341 |     if secure_mode_enabled; then
342 |         check_mounted_folder_readable "/import"
343 |     fi
344 |     : ${NEO4J_dbms_directories_import:="/import"}
345 | fi
346 | 
347 | if [ -d /metrics ]; then
348 |     if secure_mode_enabled; then
349 |         check_mounted_folder_readable "/metrics"
350 |     fi
351 |     : ${NEO4J_dbms_directories_metrics:="/metrics"}
352 | fi
353 | 
354 | if [ -d /logs ]; then
355 |     check_mounted_folder_with_chown "/logs"
356 |     : ${NEO4J_dbms_directories_logs:="/logs"}
357 |     if [ -d /data/databases ]; then
358 |         check_mounted_folder_with_chown "/data/databases"
359 |     fi
360 |     if [ -d /data/dbms ]; then
361 |         check_mounted_folder_with_chown "/data/dbms"
362 |     fi
363 | fi
364 | 
365 | if [ -d /data ]; then
366 |     check_mounted_folder_with_chown "/data"
367 | fi
368 | 
369 | 
370 | # set the neo4j initial password only if you run the database server
371 | if [ "${cmd}" == "neo4j" ]; then
372 |     if [ "${NEO4J_AUTH:-}" == "none" ]; then
373 |         NEO4J_dbms_security_auth__enabled=false
374 |     elif [[ "${NEO4J_AUTH:-}" == neo4j/* ]]; then
375 |         password="${NEO4J_AUTH#neo4j/}"
376 |         if [ "${password}" == "neo4j" ]; then
377 |             echo >&2 "Invalid value for password. It cannot be 'neo4j', which is the default."
378 |             exit 1
379 |         fi
380 | 
381 |         if running_as_root; then
382 |             # running set-initial-password as root will create subfolders to /data as root, causing startup fail when neo4j can't read or write the /data/dbms folder
383 |             # creating the folder first will avoid that
384 |             mkdir -p /data/dbms
385 |             chown "${userid}":"${groupid}" /data/dbms
386 |         fi
387 |         # Will exit with error if users already exist (and print a message explaining that)
388 |         # we probably don't want the message though, since it throws an error message on restarting the container.
389 |         neo4j-admin set-initial-password "${password}" 2>/dev/null || true
390 |     elif [ -n "${NEO4J_AUTH:-}" ]; then
391 |         echo >&2 "Invalid value for NEO4J_AUTH: '${NEO4J_AUTH}'"
392 |         exit 1
393 |     fi
394 | fi
395 | 
396 | declare -A COMMUNITY
397 | declare -A ENTERPRISE
398 | 
399 | COMMUNITY=(
400 |      [dbms.tx_log.rotation.retention_policy]="100M size"
401 |      [dbms.memory.pagecache.size]="512M"
402 |      [dbms.connectors.default_listen_address]="0.0.0.0"
403 |      [dbms.connector.https.listen_address]="0.0.0.0:7473"
404 |      [dbms.connector.http.listen_address]="0.0.0.0:7474"
405 |      [dbms.connector.bolt.listen_address]="0.0.0.0:7687"
406 | )
407 | 
408 | ENTERPRISE=(
409 | )
410 | 
411 | for conf in ${!COMMUNITY[@]} ; do
412 |     if ! grep -q "^$conf" "${NEO4J_HOME}"/conf/neo4j.conf
413 |     then
414 |         echo -e "\n"$conf=${COMMUNITY[$conf]} >> "${NEO4J_HOME}"/conf/neo4j.conf
415 |     fi
416 | done
417 | 
418 | for conf in ${!ENTERPRISE[@]} ; do
419 |     if [ "${NEO4J_EDITION}" == "enterprise" ];
420 |     then
421 |        if ! grep -q "^$conf" "${NEO4J_HOME}"/conf/neo4j.conf
422 |        then
423 |         echo -e "\n"$conf=${ENTERPRISE[$conf]} >> "${NEO4J_HOME}"/conf/neo4j.conf
424 |        fi
425 |     fi
426 | done
427 | 
428 | #The udc.source=tarball should be replaced by udc.source=docker in both dbms.jvm.additional and wrapper.java.additional
429 | #Using sed to replace only this part will allow the custom configs to be added after, separated by a ,.
430 | if grep -q "udc.source=tarball" "${NEO4J_HOME}"/conf/neo4j.conf; then
431 |      sed -i -e 's/udc.source=tarball/udc.source=docker/g' "${NEO4J_HOME}"/conf/neo4j.conf
432 | fi
433 | #The udc.source should always be set to docker by default and we have to allow also custom configs to be added after that.
434 | #In this case, this piece of code helps to add the default value and a , to support custom configs after.
435 | if ! grep -q "dbms.jvm.additional=-Dunsupported.dbms.udc.source=docker" "${NEO4J_HOME}"/conf/neo4j.conf; then
436 |   sed -i -e 's/dbms.jvm.additional=/dbms.jvm.additional=-Dunsupported.dbms.udc.source=docker,/g' "${NEO4J_HOME}"/conf/neo4j.conf
437 | fi
438 | 
439 | # list env variables with prefix NEO4J_ and create settings from them
440 | unset NEO4J_AUTH NEO4J_SHA256 NEO4J_TARBALL
441 | for i in $( set | grep ^NEO4J_ | awk -F'=' '{print $1}' | sort -rn ); do
442 |     setting=$(echo ${i} | sed 's|^NEO4J_||' | sed 's|_|.|g' | sed 's|\.\.|_|g')
443 |     value=$(echo ${!i})
444 |     # Don't allow settings with no value or settings that start with a number (neo4j converts settings to env variables and you cannot have an env variable that starts with a number)
445 |     if [[ -n ${value} ]]; then
446 |         if [[ ! "${setting}" =~ ^[0-9]+.*$ ]]; then
447 |             if grep -q -F "${setting}=" "${NEO4J_HOME}"/conf/neo4j.conf; then
448 |                 # Remove any lines containing the setting already
449 |                 sed --in-place "/^${setting}=.*/d" "${NEO4J_HOME}"/conf/neo4j.conf
450 |             fi
451 |             # Then always append setting to file
452 |             echo "${setting}=${value}" >> "${NEO4J_HOME}"/conf/neo4j.conf
453 |         else
454 |             echo >&2 "WARNING: ${setting} not written to conf file because settings that start with a number are not permitted"
455 |         fi
456 |     fi
457 | done
458 | 
459 | if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then
460 |   # NEO4JLABS_PLUGINS should be a json array of plugins like '["graph-algorithms", "apoc", "streams", "graphql"]'
461 |   install_neo4j_labs_plugins
462 | fi
463 | 
464 | [ -f "${EXTENSION_SCRIPT:-}" ] && . ${EXTENSION_SCRIPT}
465 | 
466 | if [ "${cmd}" == "dump-config" ]; then
467 |     if ! is_writable "/conf"; then
468 |         print_permissions_advice_and_fail "/conf"
469 |     fi
470 |     cp --recursive "${NEO4J_HOME}"/conf/* /conf
471 |     echo "Config Dumped"
472 |     exit 0
473 | fi
474 | 
475 | # Use su-exec to drop privileges to neo4j user
476 | # Note that su-exec, despite its name, does not replicate the
477 | # functionality of exec, so we need to use both
478 | if [ "${cmd}" == "neo4j" ]; then
479 |   ${exec_cmd} neo4j console
480 | else
481 |   ${exec_cmd} "$@"
482 | fi
483 | 
```

--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/coredb/TestAuthentication.java:
--------------------------------------------------------------------------------

```java
  1 | package com.neo4j.docker.coredb;
  2 | 
  3 | import com.neo4j.docker.coredb.configurations.Configuration;
  4 | import com.neo4j.docker.coredb.configurations.Setting;
  5 | import com.neo4j.docker.utils.DatabaseIO;
  6 | import com.neo4j.docker.utils.Neo4jVersion;
  7 | import com.neo4j.docker.utils.SetContainerUser;
  8 | import com.neo4j.docker.utils.WaitStrategies;
  9 | import com.neo4j.docker.utils.TemporaryFolderManager;
 10 | import com.neo4j.docker.utils.TestSettings;
 11 | import org.junit.jupiter.api.Assertions;
 12 | import org.junit.jupiter.api.Assumptions;
 13 | import org.junit.jupiter.api.Test;
 14 | import org.junit.jupiter.api.extension.RegisterExtension;
 15 | import org.junit.jupiter.params.ParameterizedTest;
 16 | import org.junit.jupiter.params.provider.ValueSource;
 17 | import org.slf4j.Logger;
 18 | import org.slf4j.LoggerFactory;
 19 | import org.testcontainers.containers.ContainerLaunchException;
 20 | import org.testcontainers.containers.GenericContainer;
 21 | import org.testcontainers.containers.output.OutputFrame;
 22 | import org.testcontainers.containers.output.Slf4jLogConsumer;
 23 | import org.testcontainers.containers.output.WaitingConsumer;
 24 | 
 25 | import java.io.IOException;
 26 | import java.nio.file.Files;
 27 | import java.nio.file.Path;
 28 | import java.time.Duration;
 29 | import java.util.concurrent.TimeUnit;
 30 | 
 31 | public class TestAuthentication
 32 | {
 33 |     private static Logger log = LoggerFactory.getLogger(TestAuthentication.class);
 34 |     private static final String NEO4J_AUTH_FILE_ENV = "NEO4J_AUTH_PATH";
 35 | 
 36 |     @RegisterExtension
 37 |     public static TemporaryFolderManager temporaryFolderManager = new TemporaryFolderManager();
 38 | 
 39 |     private GenericContainer createContainer( boolean asCurrentUser )
 40 |     {
 41 |         GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID );
 42 |         container.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
 43 |                  .withExposedPorts( 7474, 7687 )
 44 |                  .withLogConsumer( new Slf4jLogConsumer( log ) )
 45 |                  .waitingFor(WaitStrategies.waitForBoltReady());
 46 |         if(asCurrentUser)
 47 |         {
 48 |             SetContainerUser.nonRootUser( container );
 49 |         }
 50 |         return container;
 51 |     }
 52 | 
 53 |     private Path setInitialPasswordWithSecretsFile(GenericContainer container, String password) throws IOException
 54 |     {
 55 |         Path secretsFolder = temporaryFolderManager.createFolderAndMountAsVolume( container, "/secrets" );
 56 |         Files.writeString( secretsFolder.resolve( "passwordfile" ), "neo4j/"+password );
 57 |         container.withEnv( NEO4J_AUTH_FILE_ENV, "/secrets/passwordfile" );
 58 |         return secretsFolder;
 59 |     }
 60 | 
 61 | 	@Test
 62 | 	void testNoPassword() throws IOException
 63 |     {
 64 | 		// we test that setting NEO4J_AUTH to "none" lets the database start in TestBasic.java,
 65 |         // but that does not test that we can read/write the database
 66 | 		try(GenericContainer container = createContainer( false ))
 67 | 		{
 68 | 			container.withEnv( "NEO4J_AUTH", "none");
 69 |             temporaryFolderManager.createFolderAndMountAsVolume( container, "/data" );
 70 |             temporaryFolderManager.createFolderAndMountAsVolume( container, "/logs" );
 71 | 
 72 | 			container.start();
 73 |             DatabaseIO db = new DatabaseIO(container);
 74 |             db.putInitialDataIntoContainer( "neo4j", "none" );
 75 | 			db.verifyInitialDataInContainer( "neo4j", "none" );
 76 | 		}
 77 | 	}
 78 | 
 79 | 	@Test
 80 |     void testPasswordCantBeNeo4j() throws Exception
 81 |     {
 82 |         try(GenericContainer failContainer = new GenericContainer( TestSettings.IMAGE_ID ).withLogConsumer( new Slf4jLogConsumer( log ) ))
 83 |         {
 84 |             if ( TestSettings.EDITION == TestSettings.Edition.ENTERPRISE )
 85 |             {
 86 |                 failContainer.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" );
 87 |             }
 88 |             failContainer.withEnv( "NEO4J_AUTH", "neo4j/neo4j" );
 89 |             failContainer.start();
 90 | 
 91 |             WaitingConsumer waitingConsumer = new WaitingConsumer();
 92 |             failContainer.followOutput( waitingConsumer );
 93 | 
 94 |             Assertions.assertDoesNotThrow( () -> waitingConsumer.waitUntil(
 95 |                     frame -> frame.getUtf8String().contains("Invalid value for password" ), 20, TimeUnit.SECONDS ),
 96 |                                            "did not error due to invalid password" );
 97 |         }
 98 |     }
 99 | 
100 | 	@Test
101 | 	void testDefaultPasswordAndPasswordResetIfNoNeo4jAuthSet()
102 | 	{
103 | 		try(GenericContainer container = createContainer( true ))
104 |         {
105 |             log.info( "Starting first container as current user and not specifying NEO4J_AUTH" );
106 |             container.waitingFor( WaitStrategies.waitForNeo4jReady( "neo4j" ) );
107 |             container.start();
108 |             DatabaseIO db = new DatabaseIO(container);
109 |             // try with no password, this should fail because the default password should be applied with no NEO4J_AUTH env variable
110 | 			Assertions.assertThrows( org.neo4j.driver.exceptions.AuthenticationException.class,
111 | 									 () -> db.putInitialDataIntoContainer( "neo4j", "none" ),
112 | 									 "Able to access database with no password, even though NEO4J_AUTH=none was not specified!");
113 | 			Assertions.assertThrows( org.neo4j.driver.exceptions.ClientException.class,
114 | 									 () -> db.putInitialDataIntoContainer( "neo4j", "neo4j" ),
115 | 									 "Was not prompted for a new password when using default");
116 | 			db.changePassword( "neo4j", "neo4j", "newpassword" );
117 |             db.putInitialDataIntoContainer( "neo4j", "newpassword" );
118 |         }
119 | 	}
120 | 
121 | 	@ParameterizedTest(name = "as_current_user_{0}")
122 |     @ValueSource(booleans = {true, false})
123 |     void testCanSetPassword( boolean asCurrentUser ) throws Exception
124 |     {
125 |         // create container and mount /data folder so that data can persist between sessions
126 |         String password = "some_valid_password";
127 |         Path dataMount;
128 | 
129 |         try(GenericContainer firstContainer = createContainer( asCurrentUser ))
130 | 		{
131 | 			firstContainer.withEnv( "NEO4J_AUTH", "neo4j/"+password )
132 |                           .waitingFor(WaitStrategies.waitForNeo4jReady(password));
133 | 			dataMount = temporaryFolderManager.createFolderAndMountAsVolume(firstContainer, "/data");
134 | 			log.info( String.format( "Starting first container as %s user and setting password",
135 | 									 asCurrentUser? "current" : "default" ) );
136 |             // create a database with stuff in
137 |             firstContainer.start();
138 |             DatabaseIO db = new DatabaseIO(firstContainer);
139 |             db.putInitialDataIntoContainer( "neo4j", password );
140 |         }
141 | 
142 |         // with a new container, check the database data.
143 |         try(GenericContainer secondContainer = createContainer( asCurrentUser )
144 |                 .waitingFor(WaitStrategies.waitForNeo4jReady(password)))
145 |         {
146 |             temporaryFolderManager.mountHostFolderAsVolume( secondContainer, dataMount, "/data" );
147 |             log.info( "starting new container with same /data mount as same user without setting password" );
148 |             secondContainer.start();
149 |             DatabaseIO db = new DatabaseIO(secondContainer);
150 |             db.verifyInitialDataInContainer( "neo4j", password );
151 |         }
152 |     }
153 | 
154 |     @ParameterizedTest(name = "as_current_user_{0}")
155 |     @ValueSource(booleans = {true, false})
156 |     void testCanSetPasswordFromSecretsFile( boolean asCurrentUser ) throws Exception
157 |     {
158 |         String password = "some_valid_password";
159 | 
160 |         try(GenericContainer container = createContainer( asCurrentUser )
161 |                 .waitingFor(WaitStrategies.waitForNeo4jReady(password)))
162 | 		{
163 | 			setInitialPasswordWithSecretsFile( container, password );
164 | 			log.info( String.format( "Starting first container as %s user and setting password",
165 | 									 asCurrentUser? "current" : "default" ) );
166 |             container.start();
167 |             DatabaseIO db = new DatabaseIO(container);
168 |             db.putInitialDataIntoContainer( "neo4j", password );
169 |             db.verifyInitialDataInContainer( "neo4j", password );
170 |         }
171 |     }
172 | 
173 |     @Test
174 |     void testSecretsFileTakesPriorityOverEnvAuthentication() throws Exception
175 |     {
176 |         String password = "some_valid_password";
177 |         String wrongPassword = "not_the_password";
178 | 
179 |         try(GenericContainer container = createContainer(false )
180 |                 .waitingFor(WaitStrategies.waitForNeo4jReady(password)))
181 | 		{
182 |             container.withEnv( "NEO4J_AUTH", "neo4j/" + wrongPassword );
183 | 			setInitialPasswordWithSecretsFile( container, password );
184 |             container.start();
185 |             DatabaseIO db = new DatabaseIO(container);
186 | 			Assertions.assertThrows( org.neo4j.driver.exceptions.AuthenticationException.class,
187 | 									 () -> db.putInitialDataIntoContainer("neo4j", wrongPassword),
188 | 									 "Able to access database with password set in the environment rather than secrets");
189 | 
190 |             db.putInitialDataIntoContainer( "neo4j", password );
191 |             db.verifyInitialDataInContainer( "neo4j", password );
192 |         }
193 |     }
194 | 
195 |     @Test
196 |     void testFailsIfSecretsFileSetButMissing()
197 |     {
198 |         try(GenericContainer failContainer = createContainer( false ))
199 | 		{
200 |             WaitStrategies.waitUntilContainerFinished( failContainer, Duration.ofSeconds( 30 ) );
201 |             failContainer.withEnv( NEO4J_AUTH_FILE_ENV, "/secrets/doesnotexist.secret" );
202 | 
203 |             Assertions.assertThrows( ContainerLaunchException.class, failContainer::start,
204 |                                      "Neo4j started even though the password file does not exist" );
205 |             // an error message should be printed to stderr
206 |             String errors = failContainer.getLogs( OutputFrame.OutputType.STDERR);
207 |             Assertions.assertTrue( errors.contains( "The password file '/secrets/doesnotexist.secret' does not exist" ),
208 |                                    "Did not error about missing password file. Actual error was: "+errors);
209 |         }
210 |     }
211 | 
212 |     @Test
213 |     void testCanSetPasswordWithDebugging() throws Exception
214 |     {
215 |         String password = "some_valid_password";
216 | 
217 |         try ( GenericContainer container = createContainer( false  ) )
218 |         {
219 |             container.withEnv( "NEO4J_AUTH", "neo4j/" + password )
220 |                      .withEnv( "NEO4J_DEBUG", "yes" )
221 |                      .waitingFor(WaitStrategies.waitForNeo4jReady( password ));
222 |             // create a database with stuff in
223 |             container.start();
224 |             DatabaseIO db = new DatabaseIO( container );
225 |             db.putInitialDataIntoContainer( "neo4j", password );
226 |         }
227 |     }
228 | 
229 |     @ParameterizedTest(name = "as_current_user_{0}")
230 |     @ValueSource(booleans = {true, false})
231 |     void testSettingNeo4jAuthDoesntOverrideExistingPassword( boolean asCurrentUser ) throws Exception
232 |     {
233 |         String password = "some_valid_password";
234 |         Path dataMount;
235 | 
236 | 		try(GenericContainer firstContainer = createContainer( asCurrentUser ))
237 | 		{
238 | 			firstContainer.withEnv( "NEO4J_AUTH", "neo4j/"+password )
239 |                           .waitingFor(WaitStrategies.waitForNeo4jReady( password));
240 | 			dataMount = temporaryFolderManager.createFolderAndMountAsVolume(firstContainer, "/data");
241 | 
242 | 			// create a database with stuff in
243 | 			log.info( String.format( "Starting first container as %s user and setting password",
244 | 									 asCurrentUser? "current" : "default" ) );
245 | 			firstContainer.start();
246 | 			DatabaseIO db = new DatabaseIO(firstContainer);
247 | 			db.putInitialDataIntoContainer( "neo4j", password );
248 |         }
249 | 
250 |         // with a new container, check the database data.
251 |         try(GenericContainer secondContainer = createContainer( asCurrentUser ))
252 |         {
253 |             String wrongPassword = "not_the_password";
254 |             secondContainer.withEnv( "NEO4J_AUTH", "neo4j/"+wrongPassword );
255 |             temporaryFolderManager.mountHostFolderAsVolume( secondContainer, dataMount, "/data" );
256 |             log.info( "starting new container with same /data mount as same user without setting password" );
257 |             secondContainer.start();
258 |             DatabaseIO db = new DatabaseIO(secondContainer);
259 |             db.verifyInitialDataInContainer( "neo4j", password );
260 |         Assertions.assertThrows( org.neo4j.driver.exceptions.AuthenticationException.class,
261 |                 () -> db.verifyConnectivity( "neo4j", wrongPassword) );
262 |         }
263 |     }
264 | 
265 |     @Test
266 |     void testPromptsForPasswordReset()
267 |     {
268 |         Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3,6,0 ) ),
269 |                                 "Require password reset is only a feature in 3.6 onwards");
270 |         try(GenericContainer container = createContainer( false ))
271 |         {
272 |             String user = "neo4j";
273 |             String intialPass = "apassword";
274 |             String resetPass = "new_password";
275 |             container.withEnv("NEO4J_AUTH", user+"/"+intialPass+"/true" )
276 |                      .waitingFor(   WaitStrategies.waitForNeo4jReady( intialPass ) );
277 |             container.start();
278 |             DatabaseIO db = new DatabaseIO(container);
279 |             Assertions.assertThrows( org.neo4j.driver.exceptions.ClientException.class,
280 |                                      () -> db.putInitialDataIntoContainer( user, intialPass ),
281 |                                      "Neo4j did not error because of password reset requirement");
282 | 
283 |             db.changePassword( user, intialPass, resetPass );
284 |             db.putInitialDataIntoContainer( user, resetPass );
285 |             db.verifyInitialDataInContainer( user, resetPass );
286 |         }
287 |     }
288 | 
289 |     @ParameterizedTest(name = "use_secrets_file_{0}")
290 |     @ValueSource(booleans = {true, false})
291 |     void testWarnAndFailIfPasswordLessThan8Chars(boolean usePasswordFile) throws Exception
292 |     {
293 |         Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 5,2,0 ) ),
294 |                                 "Minimum password length introduced in 5.2.0");
295 |         String shortPassword = "123";
296 |         try(GenericContainer failContainer = createContainer( false ))
297 |         {
298 |             if(usePasswordFile)
299 |             {
300 |                 setInitialPasswordWithSecretsFile( failContainer, shortPassword );
301 |             }
302 |             else
303 |             {
304 |                 failContainer.withEnv( "NEO4J_AUTH", "neo4j/"+shortPassword );
305 |             }
306 |             WaitStrategies.waitUntilContainerFinished( failContainer, Duration.ofSeconds( 30 ) );
307 |             Assertions.assertThrows( ContainerLaunchException.class, failContainer::start,
308 |                                      "Neo4j started even though initial password was too short" );
309 |             String logsOut = failContainer.getLogs();
310 |             Assertions.assertTrue( logsOut.contains( "Invalid value for password" ),
311 |                                    "did not error due to too short password");
312 |             Assertions.assertFalse( logsOut.contains( "Remote interface available at http://localhost:7474/" ),
313 |                                     "Neo4j started even though an invalid password was set");
314 |         }
315 |     }
316 | 
317 |     @ParameterizedTest(name = "use_secrets_file_{0}")
318 |     @ValueSource(booleans = {true, false})
319 |     void testWarnAndFailIfPasswordLessThanOverride(boolean usePasswordFile) throws Exception
320 |     {
321 |         Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 5,2,0 ) ),
322 |                                 "Minimum password length introduced in 5.2.0");
323 |         String shortPassword = "123";
324 |         try(GenericContainer failContainer = createContainer( false ))
325 |         {
326 |             if(usePasswordFile)
327 |             {
328 |                 setInitialPasswordWithSecretsFile( failContainer, shortPassword );
329 |             }
330 |             else
331 |             {
332 |                 failContainer.withEnv( "NEO4J_AUTH", "neo4j/"+shortPassword );
333 |             }
334 | 
335 |             WaitStrategies.waitUntilContainerFinished( failContainer, Duration.ofSeconds( 30 ) )
336 |                           .withEnv(Configuration.getConfigurationNameMap().get( Setting.MINIMUM_PASSWORD_LENGTH ).envName, "20");
337 |             Assertions.assertThrows( ContainerLaunchException.class, failContainer::start,
338 |                                      "Neo4j started even though initial password was too short" );
339 |             String logsOut = failContainer.getLogs();
340 |             Assertions.assertTrue( logsOut.contains( "Invalid value for password" ),
341 |                                    "did not error due to too short password");
342 |             Assertions.assertFalse( logsOut.contains( "Remote interface available at http://localhost:7474/" ),
343 |                                     "Neo4j started even though an invalid password was set");
344 |         }
345 |     }
346 | 
347 |     @ParameterizedTest(name = "use_secrets_file_{0}")
348 |     @ValueSource(booleans = {true, false})
349 |     void shouldNotWarnAboutMinimumPasswordLengthIfSettingOverridden_env(boolean usePasswordFile) throws Exception
350 |     {
351 |         Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 5,2,0 ) ),
352 |                                 "Minimum password length introduced in 5.2.0");
353 |         String shortPassword = "123";
354 |         try(GenericContainer container = createContainer( false ))
355 |         {
356 |             if(usePasswordFile)
357 |             {
358 |                 setInitialPasswordWithSecretsFile( container, shortPassword );
359 |             }
360 |             else
361 |             {
362 |                 container.withEnv( "NEO4J_AUTH", "neo4j/"+shortPassword );
363 |             }
364 |             container.withEnv(Configuration.getConfigurationNameMap().get( Setting.MINIMUM_PASSWORD_LENGTH ).envName, "2");
365 |             verifyDoesNotWarnAboutMinimumPasswordLengthIfSettingOverridden( container, shortPassword );
366 |         }
367 |     }
368 | 
369 |     @ParameterizedTest(name = "use_secrets_file_{0}")
370 |     @ValueSource(booleans = {true, false})
371 |     void shouldNotWarnAboutMinimumPasswordLengthIfSettingOverridden_conf(boolean usePasswordFile) throws Exception
372 |     {
373 |         Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 5,2,0 ) ),
374 |                                 "Minimum password length introduced in 5.2.0");
375 |         String shortPassword = "123";
376 |         try(GenericContainer container = createContainer( false ))
377 |         {
378 |             if(usePasswordFile)
379 |             {
380 |                 setInitialPasswordWithSecretsFile( container, shortPassword );
381 |             }
382 |             else
383 |             {
384 |                 container.withEnv( "NEO4J_AUTH", "neo4j/"+shortPassword );
385 |             }
386 |             Path confMount = temporaryFolderManager.createFolderAndMountAsVolume(container, "/conf");
387 |             Files.writeString(confMount.resolve( "neo4j.conf" ),
388 |                               Configuration.getConfigurationNameMap().get( Setting.MINIMUM_PASSWORD_LENGTH ).name+"=2");
389 |             verifyDoesNotWarnAboutMinimumPasswordLengthIfSettingOverridden( container, shortPassword );
390 |         }
391 |     }
392 | 
393 |     private void verifyDoesNotWarnAboutMinimumPasswordLengthIfSettingOverridden(GenericContainer container, String password) throws Exception
394 |     {
395 |             container.start();
396 |             String logs = container.getLogs();
397 |             Assertions.assertFalse( logs.contains( "Invalid value for password. The minimum password length is 8 characters." ),
398 |                                     "Should not error about minimum password length if overridden.");
399 |             DatabaseIO db = new DatabaseIO( container );
400 |             db.putInitialDataIntoContainer( "neo4j", password );
401 |             db.verifyInitialDataInContainer( "neo4j", password );
402 |     }
403 | }
404 | 
```

--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/3.3/docker-entrypoint.sh:
--------------------------------------------------------------------------------

```bash
  1 | #!/bin/bash -eu
  2 | 
  3 | cmd="$1"
  4 | 
  5 | function running_as_root
  6 | {
  7 |     test "$(id -u)" = "0"
  8 | }
  9 | 
 10 | function secure_mode_enabled
 11 | {
 12 |     test "${SECURE_FILE_PERMISSIONS:=no}" = "yes"
 13 | }
 14 | 
 15 | function containsElement
 16 | {
 17 |   local e match="$1"
 18 |   shift
 19 |   for e; do [[ "$e" == "$match" ]] && return 0; done
 20 |   return 1
 21 | }
 22 | 
 23 | function is_readable
 24 | {
 25 |     # this code is fairly ugly but works no matter who this script is running as.
 26 |     # It would be nice if the writability tests could use this logic somehow.
 27 |     local _file=${1}
 28 |     perm=$(stat -c %a "${_file}")
 29 | 
 30 |     # everyone permission
 31 |     if [[ ${perm:2:1} -ge 4 ]]; then
 32 |         return 0
 33 |     fi
 34 |     # owner permissions
 35 |     if [[ ${perm:0:1} -ge 4 ]]; then
 36 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 37 |             return 0
 38 |         fi
 39 |     fi
 40 |     # group permissions
 41 |     if [[ ${perm:1:1} -ge 4 ]]; then
 42 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 43 |             return 0
 44 |         fi
 45 |     fi
 46 |     return 1
 47 | }
 48 | 
 49 | function is_writable
 50 | {
 51 |     # It would be nice if this and the is_readable function could combine somehow
 52 |     local _file=${1}
 53 |     perm=$(stat -c %a "${_file}")
 54 | 
 55 |     # everyone permission
 56 |     if containsElement ${perm:2:1} 2 3 6 7; then
 57 |         return 0
 58 |     fi
 59 |     # owner permissions
 60 |     if containsElement ${perm:0:1} 2 3 6 7; then
 61 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 62 |             return 0
 63 |         fi
 64 |     fi
 65 |     # group permissions
 66 |     if containsElement ${perm:1:1} 2 3 6 7; then
 67 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 68 |             return 0
 69 |         fi
 70 |     fi
 71 |     return 1
 72 | }
 73 | 
 74 | 
 75 | function print_permissions_advice_and_fail
 76 | {
 77 |     _directory=${1}
 78 |     echo >&2 "
 79 | Folder ${_directory} is not accessible for user: ${userid} or group ${groupid} or groups ${groups[@]}, this is commonly a file permissions issue on the mounted folder.
 80 | 
 81 | Hints to solve the issue:
 82 | 1) Make sure the folder exists before mounting it. Docker will create the folder using root permissions before starting the Neo4j container. The root permissions disallow Neo4j from writing to the mounted folder.
 83 | 2) Pass the folder owner's user ID and group ID to docker run, so that docker runs as that user.
 84 | If the folder is owned by the current user, this can be done by adding this flag to your docker run command:
 85 |   --user=\$(id -u):\$(id -g)
 86 |        "
 87 |     exit 1
 88 | }
 89 | 
 90 | function check_mounted_folder_readable
 91 | {
 92 |     local _directory=${1}
 93 |     if ! is_readable "${_directory}"; then
 94 |         print_permissions_advice_and_fail "${_directory}"
 95 |     fi
 96 | }
 97 | 
 98 | function check_mounted_folder_with_chown
 99 | {
100 | # The /data and /log directory are a bit different because they are very likely to be mounted by the user but not
101 | # necessarily writable.
102 | # This depends on whether a user ID is passed to the container and which folders are mounted.
103 | #
104 | #   No user ID passed to container:
105 | #   1) No folders are mounted.
106 | #      The /data and /log folder are owned by neo4j by default, so should be writable already.
107 | #   2) Both /log and /data are mounted.
108 | #      This means on start up, /data and /logs are owned by an unknown user and we should chown them to neo4j for
109 | #      backwards compatibility.
110 | #
111 | #   User ID passed to container:
112 | #   1) Both /data and /logs are mounted
113 | #      The /data and /logs folders are owned by an unknown user but we *should* have rw permission to them.
114 | #      That should be verified and error (helpfully) if not.
115 | #   2) User mounts /data or /logs *but not both*
116 | #      The  unmounted folder is still owned by neo4j, which should already be writable. The mounted folder should
117 | #      have rw permissions through user id. This should be verified.
118 | #   3) No folders are mounted.
119 | #      The /data and /log folder are owned by neo4j by default, and these are already writable by the user.
120 | #      (This is a very unlikely use case).
121 | 
122 |     local mountFolder=${1}
123 |     if running_as_root; then
124 |         if ! is_writable "${mountFolder}" && ! secure_mode_enabled; then
125 |             # warn that we're about to chown the folder and then chown it
126 |             echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
127 |             chown -R "${userid}":"${groupid}" "${mountFolder}"
128 |         fi
129 |     else
130 |         if [[ ! -w "${mountFolder}" ]]  && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
131 |             print_permissions_advice_and_fail "${mountFolder}"
132 |         fi
133 |     fi
134 | }
135 | 
136 | function load_plugin_from_github
137 | {
138 |   # Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
139 |   # correct format.
140 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
141 | 
142 |   local _plugins_dir="${NEO4J_HOME}/plugins"
143 |   if [ -d /plugins ]; then
144 |     local _plugins_dir="/plugins"
145 |   fi
146 |   local _versions_json_url="$(jq --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.versions" /neo4j-plugins.json )"
147 |   # Using the same name for the plugin irrespective of version ensures we don't end up with different versions of the same plugin
148 |   local _destination="${_plugins_dir}/${_plugin_name}.jar"
149 |   local _neo4j_version="$(neo4j --version | cut -d' ' -f2)"
150 | 
151 |   # Now we call out to github to get the versions.json for this plugin and we parse that to find the url for the correct plugin jar for our neo4j version
152 |   echo "Fetching versions.json for Plugin '${_plugin_name}' from ${_versions_json_url}"
153 |   local _versions_json="$(curl --silent --show-error --fail --retry 30 --retry-max-time 300 -L "${_versions_json_url}")"
154 |   local _plugin_jar_url="$(echo "${_versions_json}" | jq --raw-output ".[] | select(.neo4j==\"${_neo4j_version}\") | .jar")"
155 |   if [[ -z "${_plugin_jar_url}" ]]; then
156 |     echo >&2 "Error: No jar URL found for version '${_neo4j_version}' in versions.json from '${_versions_json_url}'"
157 |     echo >&2 "${_versions_json}"
158 |     exit 1
159 |   fi
160 |   echo "Installing Plugin '${_plugin_name}' from ${_plugin_jar_url} to ${_destination} "
161 |   curl --silent --show-error --fail --retry 30 --retry-max-time 300 -L -o "${_destination}" "${_plugin_jar_url}"
162 | 
163 |   if ! is_readable "${_destination}"; then
164 |     echo >&2 "Plugin at '${_destination}' is not readable"
165 |     exit 1
166 |   fi
167 | }
168 | 
169 | function apply_plugin_default_configuration
170 | {
171 |   # Set the correct Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
172 |   # correct format.
173 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
174 |   local _reference_conf="${2}" # used to determine if we can override properties
175 |   local _neo4j_conf="${NEO4J_HOME}/conf/neo4j.conf"
176 | 
177 |   local _property _value
178 |   echo "Applying default values for plugin ${_plugin_name} to neo4j.conf"
179 |   for _entry in $(jq  --compact-output --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.properties | to_entries[]" /neo4j-plugins.json); do
180 |     _property="$(jq --raw-output '.key' <<< "${_entry}")"
181 |     _value="$(jq --raw-output '.value' <<< "${_entry}")"
182 | 
183 |     # the first grep strips out comments
184 |     if grep -o "^[^#]*" "${_reference_conf}" | grep -q --fixed-strings "${_property}=" ; then
185 |       # property is already set in the user provided config. In this case we don't override what has been set explicitly by the user.
186 |       echo "Skipping ${_property} for plugin ${_plugin_name} because it is already set"
187 |     else
188 |       if grep -o "^[^#]*" "${_neo4j_conf}" | grep -q --fixed-strings "${_property}=" ; then
189 |         sed --in-place "s/${_property}=/&${_value},/" "${_neo4j_conf}"
190 |       else
191 |         echo "${_property}=${_value}" >> "${_neo4j_conf}"
192 |       fi
193 |     fi
194 |   done
195 | }
196 | 
197 | function install_neo4j_labs_plugins
198 | {
199 |   # We store a copy of the config before we modify it for the plugins to allow us to see if there are user-set values in the input config that we shouldn't override
200 |   local _old_config="$(mktemp)"
201 |   cp "${NEO4J_HOME}"/conf/neo4j.conf "${_old_config}"
202 |   for plugin_name in $(echo "${NEO4JLABS_PLUGINS}" | jq --raw-output '.[]'); do
203 |     load_plugin_from_github "${plugin_name}"
204 |     apply_plugin_default_configuration "${plugin_name}" "${_old_config}"
205 |   done
206 |   rm "${_old_config}"
207 | }
208 | 
209 | # If we're running as root, then run as the neo4j user. Otherwise
210 | # docker is running with --user and we simply use that user.  Note
211 | # that su-exec, despite its name, does not replicate the functionality
212 | # of exec, so we need to use both
213 | if running_as_root; then
214 |   userid="neo4j"
215 |   groupid="neo4j"
216 |   groups=($(id -G neo4j))
217 |   exec_cmd="exec gosu neo4j:neo4j"
218 | else
219 |   userid="$(id -u)"
220 |   groupid="$(id -g)"
221 |   groups=($(id -G))
222 |   exec_cmd="exec"
223 | fi
224 | readonly userid
225 | readonly groupid
226 | readonly groups
227 | readonly exec_cmd
228 | 
229 | 
230 | # Need to chown the home directory - but a user might have mounted a
231 | # volume here (notably a conf volume). So take care not to chown
232 | # volumes (stuff not owned by neo4j)
233 | if running_as_root; then
234 |     # Non-recursive chown for the base directory
235 |     chown "${userid}":"${groupid}" "${NEO4J_HOME}"
236 |     chmod 700 "${NEO4J_HOME}"
237 |     find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -user root -type d -exec chown -R ${userid}:${groupid} {} \;
238 |     find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -user root -type d -exec chmod -R 700 {} \;
239 | fi
240 | 
241 | # Only prompt for license agreement if command contains "neo4j" in it
242 | if [[ "${cmd}" == *"neo4j"* ]]; then
243 |   if [ "${NEO4J_EDITION}" == "enterprise" ]; then
244 |     if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then
245 |       echo >&2 "
246 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
247 | 
248 | (c) Neo4j Sweden AB. 2021.  All Rights Reserved.
249 | Use of this Software without a proper commercial license with Neo4j,
250 | Inc. or its affiliates is prohibited.
251 | 
252 | Email inquiries can be directed to: [email protected]
253 | 
254 | More information is also available at: https://neo4j.com/licensing/
255 | 
256 | 
257 | To accept the license agreement set the environment variable
258 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
259 | 
260 | To do this you can use the following docker argument:
261 | 
262 |         --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
263 | "
264 |       exit 1
265 |     fi
266 |   fi
267 | fi
268 | 
269 | # Env variable naming convention:
270 | # - prefix NEO4J_
271 | # - double underscore char '__' instead of single underscore '_' char in the setting name
272 | # - underscore char '_' instead of dot '.' char in the setting name
273 | # Example:
274 | # NEO4J_dbms_tx__log_rotation_retention__policy env variable to set
275 | #       dbms.tx_log.rotation.retention_policy setting
276 | 
277 | # Backward compatibility - map old hardcoded env variables into new naming convention (if they aren't set already)
278 | # Set some to default values if unset
279 | : ${NEO4J_dbms_tx__log_rotation_retention__policy:=${NEO4J_dbms_txLog_rotation_retentionPolicy:-"100M size"}}
280 | : ${NEO4J_wrapper_java_additional:=${NEO4J_UDC_SOURCE:-"-Dneo4j.ext.udc.source=docker"}}
281 | : ${NEO4J_dbms_unmanaged__extension__classes:=${NEO4J_dbms_unmanagedExtensionClasses:-}}
282 | : ${NEO4J_dbms_allow__format__migration:=${NEO4J_dbms_allowFormatMigration:-}}
283 | : ${NEO4J_dbms_connectors_default__advertised__address:=${NEO4J_dbms_connectors_defaultAdvertisedAddress:-}}
284 | : ${NEO4J_ha_server__id:=${NEO4J_ha_serverId:-}}
285 | : ${NEO4J_ha_initial__hosts:=${NEO4J_ha_initialHosts:-}}
286 | 
287 | if [ "${NEO4J_EDITION}" == "enterprise" ];
288 |   then
289 |    : ${NEO4J_causal__clustering_expected__core__cluster__size:=${NEO4J_causalClustering_expectedCoreClusterSize:-}}
290 |    : ${NEO4J_causal__clustering_initial__discovery__members:=${NEO4J_causalClustering_initialDiscoveryMembers:-}}
291 |    : ${NEO4J_causal__clustering_discovery__advertised__address:=${NEO4J_causalClustering_discoveryAdvertisedAddress:-"$(hostname):5000"}}
292 |    : ${NEO4J_causal__clustering_transaction__advertised__address:=${NEO4J_causalClustering_transactionAdvertisedAddress:-"$(hostname):6000"}}
293 |    : ${NEO4J_causal__clustering_raft__advertised__address:=${NEO4J_causalClustering_raftAdvertisedAddress:-"$(hostname):7000"}}
294 |    # Custom settings for dockerized neo4j
295 |    : ${NEO4J_ha_host_coordination:=$(hostname):5001}
296 |    : ${NEO4J_ha_host_data:=$(hostname):6001}
297 |    : ${NEO4J_causal__clustering_discovery__advertised__address:=$(hostname):5000}
298 |    : ${NEO4J_causal__clustering_transaction__advertised__address:=$(hostname):6000}
299 |    : ${NEO4J_causal__clustering_raft__advertised__address:=$(hostname):7000}
300 | fi
301 | 
302 | # unset old hardcoded unsupported env variables
303 | unset NEO4J_dbms_txLog_rotation_retentionPolicy NEO4J_UDC_SOURCE \
304 |     NEO4J_dbms_unmanagedExtensionClasses NEO4J_dbms_allowFormatMigration \
305 |     NEO4J_dbms_connectors_defaultAdvertisedAddress NEO4J_ha_serverId \
306 |     NEO4J_ha_initialHosts NEO4J_causalClustering_expectedCoreClusterSize \
307 |     NEO4J_causalClustering_initialDiscoveryMembers \
308 |     NEO4J_causalClustering_discoveryListenAddress \
309 |     NEO4J_causalClustering_discoveryAdvertisedAddress \
310 |     NEO4J_causalClustering_transactionListenAddress \
311 |     NEO4J_causalClustering_transactionAdvertisedAddress \
312 |     NEO4J_causalClustering_raftListenAddress \
313 |     NEO4J_causalClustering_raftAdvertisedAddress
314 | 
315 | if [ -d /conf ]; then
316 |     if secure_mode_enabled; then
317 | 	    check_mounted_folder_readable "/conf"
318 |     fi
319 |     find /conf -type f -exec cp {} "${NEO4J_HOME}"/conf \;
320 | fi
321 | 
322 | if [ -d /ssl ]; then
323 |     if secure_mode_enabled; then
324 |     	check_mounted_folder_readable "/ssl"
325 |     fi
326 |     : ${NEO4J_dbms_directories_certificates:="/ssl"}
327 | fi
328 | 
329 | if [ -d /plugins ]; then
330 |     if secure_mode_enabled; then
331 |         if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then
332 |             # We need write permissions
333 |             check_mounted_folder_with_chown "/plugins"
334 |         fi
335 |         check_mounted_folder_readable "/plugins"
336 |     fi
337 |     : ${NEO4J_dbms_directories_plugins:="/plugins"}
338 | fi
339 | 
340 | if [ -d /import ]; then
341 |     if secure_mode_enabled; then
342 |         check_mounted_folder_readable "/import"
343 |     fi
344 |     : ${NEO4J_dbms_directories_import:="/import"}
345 | fi
346 | 
347 | if [ -d /metrics ]; then
348 |     if secure_mode_enabled; then
349 |         check_mounted_folder_readable "/metrics"
350 |     fi
351 |     : ${NEO4J_dbms_directories_metrics:="/metrics"}
352 | fi
353 | 
354 | if [ -d /logs ]; then
355 |     check_mounted_folder_with_chown "/logs"
356 |     : ${NEO4J_dbms_directories_logs:="/logs"}
357 |     if [ -d /data/databases ]; then
358 |         check_mounted_folder_with_chown "/data/databases"
359 |     fi
360 |     if [ -d /data/dbms ]; then
361 |         check_mounted_folder_with_chown "/data/dbms"
362 |     fi
363 | fi
364 | 
365 | if [ -d /data ]; then
366 |     check_mounted_folder_with_chown "/data"
367 | fi
368 | 
369 | 
370 | # set the neo4j initial password only if you run the database server
371 | if [ "${cmd}" == "neo4j" ]; then
372 |     if [ "${NEO4J_AUTH:-}" == "none" ]; then
373 |         NEO4J_dbms_security_auth__enabled=false
374 |     elif [[ "${NEO4J_AUTH:-}" == neo4j/* ]]; then
375 |         password="${NEO4J_AUTH#neo4j/}"
376 |         if [ "${password}" == "neo4j" ]; then
377 |             echo >&2 "Invalid value for password. It cannot be 'neo4j', which is the default."
378 |             exit 1
379 |         fi
380 | 
381 |         if running_as_root; then
382 |             # running set-initial-password as root will create subfolders to /data as root, causing startup fail when neo4j can't read or write the /data/dbms folder
383 |             # creating the folder first will avoid that
384 |             mkdir -p /data/dbms
385 |             chown "${userid}":"${groupid}" /data/dbms
386 |         fi
387 |         # Will exit with error if users already exist (and print a message explaining that)
388 |         # we probably don't want the message though, since it throws an error message on restarting the container.
389 |         neo4j-admin set-initial-password "${password}" 2>/dev/null || true
390 |     elif [ -n "${NEO4J_AUTH:-}" ]; then
391 |         echo >&2 "Invalid value for NEO4J_AUTH: '${NEO4J_AUTH}'"
392 |         exit 1
393 |     fi
394 | fi
395 | 
396 | declare -A COMMUNITY
397 | declare -A ENTERPRISE
398 | 
399 | COMMUNITY=(
400 |      [dbms.tx_log.rotation.retention_policy]="100M size"
401 |      [dbms.memory.pagecache.size]="512M"
402 |      [dbms.connectors.default_listen_address]="0.0.0.0"
403 |      [dbms.connector.https.listen_address]="0.0.0.0:7473"
404 |      [dbms.connector.http.listen_address]="0.0.0.0:7474"
405 |      [dbms.connector.bolt.listen_address]="0.0.0.0:7687"
406 | )
407 | 
408 | ENTERPRISE=(
409 |      [causal_clustering.transaction_listen_address]="0.0.0.0:6000"
410 |      [causal_clustering.raft_listen_address]="0.0.0.0:7000"
411 |      [causal_clustering.discovery_listen_address]="0.0.0.0:5000"
412 | )
413 | 
414 | for conf in ${!COMMUNITY[@]} ; do
415 | 
416 |     if ! grep -q "^$conf" "${NEO4J_HOME}"/conf/neo4j.conf
417 |     then
418 |         echo -e "\n"$conf=${COMMUNITY[$conf]} >> "${NEO4J_HOME}"/conf/neo4j.conf
419 |     fi
420 | done
421 | 
422 | for conf in ${!ENTERPRISE[@]} ; do
423 | 
424 |     if [ "${NEO4J_EDITION}" == "enterprise" ];
425 |     then
426 |        if ! grep -q "^$conf" "${NEO4J_HOME}"/conf/neo4j.conf
427 |        then
428 |         echo -e "\n"$conf=${ENTERPRISE[$conf]} >> "${NEO4J_HOME}"/conf/neo4j.conf
429 |        fi
430 |     fi
431 | done
432 | 
433 | #The udc.source=tarball should be replaced by udc.source=docker in both dbms.jvm.additional and wrapper.java.additional
434 | #Using sed to replace only this part will allow the custom configs to be added after, separated by a ,.
435 | if grep -q "udc.source=tarball" "${NEO4J_HOME}"/conf/neo4j.conf; then
436 |      sed -i -e 's/udc.source=tarball/udc.source=docker/g' "${NEO4J_HOME}"/conf/neo4j.conf
437 | fi
438 | #The udc.source should always be set to docker by default and we have to allow also custom configs to be added after that.
439 | #In this case, this piece of code helps to add the default value and a , to support custom configs after.
440 | if ! grep -q "dbms.jvm.additional=-Dunsupported.dbms.udc.source=docker" "${NEO4J_HOME}"/conf/neo4j.conf; then
441 |   sed -i -e 's/dbms.jvm.additional=/dbms.jvm.additional=-Dunsupported.dbms.udc.source=docker,/g' "${NEO4J_HOME}"/conf/neo4j.conf
442 | fi
443 | 
444 | # list env variables with prefix NEO4J_ and create settings from them
445 | unset NEO4J_AUTH NEO4J_SHA256 NEO4J_TARBALL
446 | for i in $( set | grep ^NEO4J_ | awk -F'=' '{print $1}' | sort -rn ); do
447 |     setting=$(echo ${i} | sed 's|^NEO4J_||' | sed 's|_|.|g' | sed 's|\.\.|_|g')
448 |     value=$(echo ${!i})
449 |     # Don't allow settings with no value or settings that start with a number (neo4j converts settings to env variables and you cannot have an env variable that starts with a number)
450 |     if [[ -n ${value} ]]; then
451 |         if [[ ! "${setting}" =~ ^[0-9]+.*$ ]]; then
452 |             if grep -q -F "${setting}=" "${NEO4J_HOME}"/conf/neo4j.conf; then
453 |                 # Remove any lines containing the setting already
454 |                 sed --in-place "/^${setting}=.*/d" "${NEO4J_HOME}"/conf/neo4j.conf
455 |             fi
456 |             # Then always append setting to file
457 |             echo "${setting}=${value}" >> "${NEO4J_HOME}"/conf/neo4j.conf
458 |         else
459 |             echo >&2 "WARNING: ${setting} not written to conf file because settings that start with a number are not permitted"
460 |         fi
461 |     fi
462 | done
463 | 
464 | if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then
465 |   # NEO4JLABS_PLUGINS should be a json array of plugins like '["graph-algorithms", "apoc", "streams", "graphql"]'
466 |   install_neo4j_labs_plugins
467 | fi
468 | 
469 | [ -f "${EXTENSION_SCRIPT:-}" ] && . ${EXTENSION_SCRIPT}
470 | 
471 | if [ "${cmd}" == "dump-config" ]; then
472 |     if ! is_writable "/conf"; then
473 |         print_permissions_advice_and_fail "/conf"
474 |     fi
475 |     cp --recursive "${NEO4J_HOME}"/conf/* /conf
476 |     echo "Config Dumped"
477 |     exit 0
478 | fi
479 | 
480 | # Use su-exec to drop privileges to neo4j user
481 | # Note that su-exec, despite its name, does not replicate the
482 | # functionality of exec, so we need to use both
483 | if [ "${cmd}" == "neo4j" ]; then
484 |   ${exec_cmd} neo4j console
485 | else
486 |   ${exec_cmd} "$@"
487 | fi
488 | 
```

--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/4.0/coredb/docker-entrypoint.sh:
--------------------------------------------------------------------------------

```bash
  1 | #!/bin/bash -eu
  2 | 
  3 | cmd="$1"
  4 | 
  5 | function running_as_root
  6 | {
  7 |     test "$(id -u)" = "0"
  8 | }
  9 | 
 10 | function secure_mode_enabled
 11 | {
 12 |     test "${SECURE_FILE_PERMISSIONS:=no}" = "yes"
 13 | }
 14 | 
 15 | function containsElement
 16 | {
 17 |   local e match="$1"
 18 |   shift
 19 |   for e; do [[ "$e" == "$match" ]] && return 0; done
 20 |   return 1
 21 | }
 22 | 
 23 | function is_readable
 24 | {
 25 |     # this code is fairly ugly but works no matter who this script is running as.
 26 |     # It would be nice if the writability tests could use this logic somehow.
 27 |     local _file=${1}
 28 |     perm=$(stat -c %a "${_file}")
 29 | 
 30 |     # everyone permission
 31 |     if [[ ${perm:2:1} -ge 4 ]]; then
 32 |         return 0
 33 |     fi
 34 |     # owner permissions
 35 |     if [[ ${perm:0:1} -ge 4 ]]; then
 36 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 37 |             return 0
 38 |         fi
 39 |     fi
 40 |     # group permissions
 41 |     if [[ ${perm:1:1} -ge 4 ]]; then
 42 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 43 |             return 0
 44 |         fi
 45 |     fi
 46 |     return 1
 47 | }
 48 | 
 49 | function is_writable
 50 | {
 51 |     # It would be nice if this and the is_readable function could combine somehow
 52 |     local _file=${1}
 53 |     perm=$(stat -c %a "${_file}")
 54 | 
 55 |     # everyone permission
 56 |     if containsElement ${perm:2:1} 2 3 6 7; then
 57 |         return 0
 58 |     fi
 59 |     # owner permissions
 60 |     if containsElement ${perm:0:1} 2 3 6 7; then
 61 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 62 |             return 0
 63 |         fi
 64 |     fi
 65 |     # group permissions
 66 |     if containsElement ${perm:1:1} 2 3 6 7; then
 67 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 68 |             return 0
 69 |         fi
 70 |     fi
 71 |     return 1
 72 | }
 73 | 
 74 | 
 75 | function print_permissions_advice_and_fail
 76 | {
 77 |     _directory=${1}
 78 |     echo >&2 "
 79 | Folder ${_directory} is not accessible for user: ${userid} or group ${groupid} or groups ${groups[@]}, this is commonly a file permissions issue on the mounted folder.
 80 | 
 81 | Hints to solve the issue:
 82 | 1) Make sure the folder exists before mounting it. Docker will create the folder using root permissions before starting the Neo4j container. The root permissions disallow Neo4j from writing to the mounted folder.
 83 | 2) Pass the folder owner's user ID and group ID to docker run, so that docker runs as that user.
 84 | If the folder is owned by the current user, this can be done by adding this flag to your docker run command:
 85 |   --user=\$(id -u):\$(id -g)
 86 |        "
 87 |     exit 1
 88 | }
 89 | 
 90 | function check_mounted_folder_readable
 91 | {
 92 |     local _directory=${1}
 93 |     if ! is_readable "${_directory}"; then
 94 |         print_permissions_advice_and_fail "${_directory}"
 95 |     fi
 96 | }
 97 | 
 98 | function check_mounted_folder_writable_with_chown
 99 | {
100 | # The /data and /log directory are a bit different because they are very likely to be mounted by the user but not
101 | # necessarily writable.
102 | # This depends on whether a user ID is passed to the container and which folders are mounted.
103 | #
104 | #   No user ID passed to container:
105 | #   1) No folders are mounted.
106 | #      The /data and /log folder are owned by neo4j by default, so should be writable already.
107 | #   2) Both /log and /data are mounted.
108 | #      This means on start up, /data and /logs are owned by an unknown user and we should chown them to neo4j for
109 | #      backwards compatibility.
110 | #
111 | #   User ID passed to container:
112 | #   1) Both /data and /logs are mounted
113 | #      The /data and /logs folders are owned by an unknown user but we *should* have rw permission to them.
114 | #      That should be verified and error (helpfully) if not.
115 | #   2) User mounts /data or /logs *but not both*
116 | #      The  unmounted folder is still owned by neo4j, which should already be writable. The mounted folder should
117 | #      have rw permissions through user id. This should be verified.
118 | #   3) No folders are mounted.
119 | #      The /data and /log folder are owned by neo4j by default, and these are already writable by the user.
120 | #      (This is a very unlikely use case).
121 | 
122 |     local mountFolder=${1}
123 |     if running_as_root; then
124 |         if ! secure_mode_enabled; then
125 |             # check folder permissions
126 |             if ! is_writable "${mountFolder}" ;  then
127 |                 # warn that we're about to chown the folder and then chown it
128 |                 echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
129 |                 chown -R "${userid}":"${groupid}" "${mountFolder}"
130 |             # check permissions on files in the folder
131 |             elif [ $(gosu "${userid}":"${groupid}" find "${mountFolder}" -not -writable | wc -l) -gt 0 ]; then
132 |                 echo "Warning: Some files inside \"${mountFolder}\" are not writable from inside container. Changing folder owner to ${userid}."
133 |                 chown -R "${userid}":"${groupid}" "${mountFolder}"
134 |             fi
135 |         fi
136 |     else
137 |         if [[ ! -w "${mountFolder}" ]]  && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
138 |             print_permissions_advice_and_fail "${mountFolder}"
139 |         fi
140 |     fi
141 | }
142 | 
143 | function load_plugin_from_github
144 | {
145 |   # Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
146 |   # correct format.
147 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
148 | 
149 |   local _plugins_dir="${NEO4J_HOME}/plugins"
150 |   if [ -d /plugins ]; then
151 |     local _plugins_dir="/plugins"
152 |   fi
153 |   local _versions_json_url="$(jq --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.versions" /neo4j-plugins.json )"
154 |   # Using the same name for the plugin irrespective of version ensures we don't end up with different versions of the same plugin
155 |   local _destination="${_plugins_dir}/${_plugin_name}.jar"
156 |   local _neo4j_version="$(neo4j --version | cut -d' ' -f2)"
157 | 
158 |   # Now we call out to github to get the versions.json for this plugin and we parse that to find the url for the correct plugin jar for our neo4j version
159 |   echo "Fetching versions.json for Plugin '${_plugin_name}' from ${_versions_json_url}"
160 |   local _versions_json="$(wget -q --timeout 300 --tries 30 -O - "${_versions_json_url}")"
161 |   local _plugin_jar_url="$(echo "${_versions_json}" | jq --raw-output ".[] | select(.neo4j==\"${_neo4j_version}\") | .jar")"
162 |   if [[ -z "${_plugin_jar_url}" ]]; then
163 |     echo >&2 "Error: No jar URL found for version '${_neo4j_version}' in versions.json from '${_versions_json_url}'"
164 |     echo >&2 "${_versions_json}"
165 |     exit 1
166 |   fi
167 |   echo "Installing Plugin '${_plugin_name}' from ${_plugin_jar_url} to ${_destination} "
168 |   wget -q --timeout 300 --tries 30 --output-document="${_destination}" "${_plugin_jar_url}"
169 | 
170 |   if ! is_readable "${_destination}"; then
171 |     echo >&2 "Plugin at '${_destination}' is not readable"
172 |     exit 1
173 |   fi
174 | }
175 | 
176 | function apply_plugin_default_configuration
177 | {
178 |   # Set the correct Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
179 |   # correct format.
180 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
181 |   local _reference_conf="${2}" # used to determine if we can override properties
182 |   local _neo4j_conf="${NEO4J_HOME}/conf/neo4j.conf"
183 | 
184 |   local _property _value
185 |   echo "Applying default values for plugin ${_plugin_name} to neo4j.conf"
186 |   for _entry in $(jq  --compact-output --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.properties | to_entries[]" /neo4j-plugins.json); do
187 |     _property="$(jq --raw-output '.key' <<< "${_entry}")"
188 |     _value="$(jq --raw-output '.value' <<< "${_entry}")"
189 | 
190 |     # the first grep strips out comments
191 |     if grep -o "^[^#]*" "${_reference_conf}" | grep -q --fixed-strings "${_property}=" ; then
192 |       # property is already set in the user provided config. In this case we don't override what has been set explicitly by the user.
193 |       echo "Skipping ${_property} for plugin ${_plugin_name} because it is already set"
194 |     else
195 |       if grep -o "^[^#]*" "${_neo4j_conf}" | grep -q --fixed-strings "${_property}=" ; then
196 |         sed --in-place "s/${_property}=/&${_value},/" "${_neo4j_conf}"
197 |       else
198 |         echo "${_property}=${_value}" >> "${_neo4j_conf}"
199 |       fi
200 |     fi
201 |   done
202 | }
203 | 
204 | function install_neo4j_labs_plugins
205 | {
206 |   # We store a copy of the config before we modify it for the plugins to allow us to see if there are user-set values in the input config that we shouldn't override
207 |   local _old_config="$(mktemp)"
208 |   cp "${NEO4J_HOME}"/conf/neo4j.conf "${_old_config}"
209 |   for plugin_name in $(echo "${NEO4JLABS_PLUGINS}" | jq --raw-output '.[]'); do
210 |     load_plugin_from_github "${plugin_name}"
211 |     apply_plugin_default_configuration "${plugin_name}" "${_old_config}"
212 |   done
213 |   rm "${_old_config}"
214 | }
215 | 
216 | function add_docker_default_to_conf
217 | {
218 |     # docker defaults should NOT overwrite values already in the conf file
219 |     local _setting="${1}"
220 |     local _value="${2}"
221 |     local _neo4j_home="${3}"
222 | 
223 |     if ! grep -q "^${_setting}=" "${_neo4j_home}"/conf/neo4j.conf
224 |     then
225 |         echo -e "\n"${_setting}=${_value} >> "${_neo4j_home}"/conf/neo4j.conf
226 |     fi
227 | }
228 | 
229 | function add_env_setting_to_conf
230 | {
231 |     # settings from environment variables should overwrite values already in the conf
232 |     local _setting=${1}
233 |     local _value=${2}
234 |     local _neo4j_home=${3}
235 | 
236 |     if grep -q -F "${_setting}=" "${_neo4j_home}"/conf/neo4j.conf; then
237 |         # Remove any lines containing the setting already
238 |         sed --in-place "/^${_setting}=.*/d" "${_neo4j_home}"/conf/neo4j.conf
239 |     fi
240 |     # Then always append setting to file
241 |     echo "${_setting}=${_value}" >> "${_neo4j_home}"/conf/neo4j.conf
242 | }
243 | 
244 | function set_initial_password
245 | {
246 |     local _neo4j_auth="${1}"
247 | 
248 |     # set the neo4j initial password only if you run the database server
249 |     if [ "${cmd}" == "neo4j" ]; then
250 |         if [ "${_neo4j_auth:-}" == "none" ]; then
251 |             add_env_setting_to_conf "dbms.security.auth_enabled" "false" "${NEO4J_HOME}"
252 |             # NEO4J_dbms_security_auth__enabled=false
253 |         elif [[ "${_neo4j_auth:-}" =~ ^([^/]+)\/([^/]+)/?([tT][rR][uU][eE])?$ ]]; then
254 |             admin_user="${BASH_REMATCH[1]}"
255 |             password="${BASH_REMATCH[2]}"
256 |             do_reset="${BASH_REMATCH[3]}"
257 | 
258 |             if [ "${password}" == "neo4j" ]; then
259 |                 echo >&2 "Invalid value for password. It cannot be 'neo4j', which is the default."
260 |                 exit 1
261 |             fi
262 |             if [ "${admin_user}" != "neo4j" ]; then
263 |                 echo >&2 "Invalid admin username, it must be neo4j"
264 |                 exit 1
265 |             fi
266 | 
267 |             if running_as_root; then
268 |                 # running set-initial-password as root will create subfolders to /data as root, causing startup fail when neo4j can't read or write the /data/dbms folder
269 |                 # creating the folder first will avoid that
270 |                 mkdir -p /data/dbms
271 |                 chown "${userid}":"${groupid}" /data/dbms
272 |             fi
273 | 
274 |             # Will exit with error if users already exist (and print a message explaining that)
275 |             # we probably don't want the message though, since it throws an error message on restarting the container.
276 |             if [ "${do_reset}" == "true" ]; then
277 |                 neo4j-admin set-initial-password "${password}" --require-password-change 2>/dev/null || true
278 |             else
279 |                 neo4j-admin set-initial-password "${password}" 2>/dev/null || true
280 |             fi
281 |         elif [ -n "${_neo4j_auth:-}" ]; then
282 |             echo "$_neo4j_auth is invalid"
283 |             echo >&2 "Invalid value for NEO4J_AUTH: '${_neo4j_auth}'"
284 |             exit 1
285 |         fi
286 |     fi
287 | }
288 | 
289 | # If we're running as root, then run as the neo4j user. Otherwise
290 | # docker is running with --user and we simply use that user.  Note
291 | # that su-exec, despite its name, does not replicate the functionality
292 | # of exec, so we need to use both
293 | if running_as_root; then
294 |   userid="neo4j"
295 |   groupid="neo4j"
296 |   groups=($(id -G neo4j))
297 |   exec_cmd="exec gosu neo4j:neo4j"
298 | else
299 |   userid="$(id -u)"
300 |   groupid="$(id -g)"
301 |   groups=($(id -G))
302 |   exec_cmd="exec"
303 | fi
304 | readonly userid
305 | readonly groupid
306 | readonly groups
307 | readonly exec_cmd
308 | 
309 | 
310 | # Need to chown the home directory
311 | if running_as_root; then
312 |     chown -R "${userid}":"${groupid}" "${NEO4J_HOME}"
313 |     chmod 700 "${NEO4J_HOME}"
314 |     find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -type d -exec chmod -R 700 {} \;
315 | fi
316 | 
317 | # Only prompt for license agreement if command contains "neo4j" in it
318 | # ==== CHECK LICENSE AGREEMENT ====
319 | 
320 | if [[ "${cmd}" == *"neo4j"* ]]; then
321 |   if [ "${NEO4J_EDITION}" == "enterprise" ]; then
322 |     if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then
323 |       echo >&2 "
324 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
325 | 
326 | (c) Neo4j Sweden AB. 2021.  All Rights Reserved.
327 | Use of this Software without a proper commercial license with Neo4j,
328 | Inc. or its affiliates is prohibited.
329 | 
330 | Email inquiries can be directed to: [email protected]
331 | 
332 | More information is also available at: https://neo4j.com/licensing/
333 | 
334 | 
335 | To accept the license agreement set the environment variable
336 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
337 | 
338 | To do this you can use the following docker argument:
339 | 
340 |         --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
341 | "
342 |       exit 1
343 |     fi
344 |   fi
345 | fi
346 | 
347 | # Env variable naming convention:
348 | # ==== RENAME LEGACY ENVIRONMENT CONF VARIABLES ====
349 | 
350 | # - prefix NEO4J_
351 | # - double underscore char '__' instead of single underscore '_' char in the setting name
352 | # - underscore char '_' instead of dot '.' char in the setting name
353 | # Example:
354 | # NEO4J_dbms_tx__log_rotation_retention__policy env variable to set
355 | #       dbms.tx_log.rotation.retention_policy setting
356 | 
357 | # Backward compatibility - map old hardcoded env variables into new naming convention (if they aren't set already)
358 | # Set some to default values if unset
359 | : ${NEO4J_dbms_tx__log_rotation_retention__policy:=${NEO4J_dbms_txLog_rotation_retentionPolicy:-}}
360 | : ${NEO4J_dbms_unmanaged__extension__classes:=${NEO4J_dbms_unmanagedExtensionClasses:-}}
361 | : ${NEO4J_dbms_allow__format__migration:=${NEO4J_dbms_allowFormatMigration:-}}
362 | : ${NEO4J_dbms_connectors_default__advertised__address:=${NEO4J_dbms_connectors_defaultAdvertisedAddress:-}}
363 | 
364 | if [ "${NEO4J_EDITION}" == "enterprise" ];
365 |   then
366 |    : ${NEO4J_causal__clustering_expected__core__cluster__size:=${NEO4J_causalClustering_expectedCoreClusterSize:-}}
367 |    : ${NEO4J_causal__clustering_initial__discovery__members:=${NEO4J_causalClustering_initialDiscoveryMembers:-}}
368 |    : ${NEO4J_causal__clustering_discovery__advertised__address:=${NEO4J_causalClustering_discoveryAdvertisedAddress:-}}
369 |    : ${NEO4J_causal__clustering_transaction__advertised__address:=${NEO4J_causalClustering_transactionAdvertisedAddress:-}}
370 |    : ${NEO4J_causal__clustering_raft__advertised__address:=${NEO4J_causalClustering_raftAdvertisedAddress:-}}
371 | fi
372 | 
373 | # unset old hardcoded unsupported env variables
374 | unset NEO4J_dbms_txLog_rotation_retentionPolicy NEO4J_UDC_SOURCE \
375 |     NEO4J_dbms_unmanagedExtensionClasses NEO4J_dbms_allowFormatMigration \
376 |     NEO4J_dbms_connectors_defaultAdvertisedAddress NEO4J_ha_serverId \
377 |     NEO4J_ha_initialHosts NEO4J_causalClustering_expectedCoreClusterSize \
378 |     NEO4J_causalClustering_initialDiscoveryMembers \
379 |     NEO4J_causalClustering_discoveryListenAddress \
380 |     NEO4J_causalClustering_discoveryAdvertisedAddress \
381 |     NEO4J_causalClustering_transactionListenAddress \
382 |     NEO4J_causalClustering_transactionAdvertisedAddress \
383 |     NEO4J_causalClustering_raftListenAddress \
384 |     NEO4J_causalClustering_raftAdvertisedAddress
385 | 
386 | 
387 | # ==== CHECK FILE PERMISSIONS ON MOUNTED FOLDERS ====
388 | 
389 | if [ -d /conf ]; then
390 |     check_mounted_folder_readable "/conf"
391 |     find /conf -type f -exec cp {} "${NEO4J_HOME}"/conf \;
392 | fi
393 | 
394 | if [ -d /ssl ]; then
395 |     check_mounted_folder_readable "/ssl"
396 |     rm -rf "${NEO4J_HOME}"/certificates
397 |     ln -s /ssl "${NEO4J_HOME}"/certificates
398 | fi
399 | 
400 | if [ -d /plugins ]; then
401 |     if [[ -n "${NEO4JLABS_PLUGINS:-}" ]]; then
402 |         # We need write permissions
403 |         check_mounted_folder_writable_with_chown "/plugins"
404 |     fi
405 |     check_mounted_folder_readable "/plugins"
406 |     : ${NEO4J_dbms_directories_plugins:="/plugins"}
407 | fi
408 | 
409 | if [ -d /import ]; then
410 |     check_mounted_folder_readable "/import"
411 |     : ${NEO4J_dbms_directories_import:="/import"}
412 | fi
413 | 
414 | if [ -d /metrics ]; then
415 |     check_mounted_folder_writable_with_chown "/metrics"
416 |     : ${NEO4J_dbms_directories_metrics:="/metrics"}
417 | fi
418 | 
419 | if [ -d /logs ]; then
420 |     check_mounted_folder_writable_with_chown "/logs"
421 |     : ${NEO4J_dbms_directories_logs:="/logs"}
422 | fi
423 | 
424 | if [ -d /data ]; then
425 |     check_mounted_folder_writable_with_chown "/data"
426 |     if [ -d /data/databases ]; then
427 |         check_mounted_folder_writable_with_chown "/data/databases"
428 |     fi
429 |     if [ -d /data/dbms ]; then
430 |         check_mounted_folder_writable_with_chown "/data/dbms"
431 |     fi
432 |     if [ -d /data/transactions ]; then
433 |         check_mounted_folder_writable_with_chown "/data/transactions"
434 |     fi
435 | fi
436 | 
437 | # ==== SET CONFIGURATIONS ====
438 | 
439 | ## == DOCKER SPECIFIC DEFAULT CONFIGURATIONS ===
440 | ## these should not override *any* configurations set by the user
441 | 
442 | add_docker_default_to_conf "dbms.tx_log.rotation.retention_policy" "100M size" "${NEO4J_HOME}"
443 | add_docker_default_to_conf "dbms.memory.pagecache.size" "512M" "${NEO4J_HOME}"
444 | add_docker_default_to_conf "dbms.default_listen_address" "0.0.0.0" "${NEO4J_HOME}"
445 | # set enterprise only docker defaults
446 | if [ "${NEO4J_EDITION}" == "enterprise" ];
447 | then
448 |     add_docker_default_to_conf "causal_clustering.discovery_advertised_address" "$(hostname):5000" "${NEO4J_HOME}"
449 |     add_docker_default_to_conf "causal_clustering.transaction_advertised_address" "$(hostname):6000" "${NEO4J_HOME}"
450 |     add_docker_default_to_conf "causal_clustering.raft_advertised_address" "$(hostname):7000" "${NEO4J_HOME}"
451 | fi
452 | 
453 | ## == ENVIRONMENT VARIABLE CONFIGURATIONS ===
454 | ## these override BOTH defaults and any existing values in the neo4j.conf file
455 | 
456 | # save NEO4J_HOME and NEO4J_AUTH to temp variables that don't begin with NEO4J_ so they don't get added to the conf
457 | temp_neo4j_home="${NEO4J_HOME}"
458 | temp_neo4j_auth="${NEO4J_AUTH:-}"
459 | # list env variables with prefix NEO4J_ and create settings from them
460 | unset NEO4J_AUTH NEO4J_SHA256 NEO4J_TARBALL NEO4J_EDITION NEO4J_ACCEPT_LICENSE_AGREEMENT NEO4J_HOME
461 | for i in $( set | grep ^NEO4J_ | awk -F'=' '{print $1}' | sort -rn ); do
462 |     setting=$(echo "${i}" | sed 's|^NEO4J_||' | sed 's|_|.|g' | sed 's|\.\.|_|g')
463 |     value=$(echo "${!i}")
464 |     # Don't allow settings with no value or settings that start with a number (neo4j converts settings to env variables and you cannot have an env variable that starts with a number)
465 |     if [[ -n ${value} ]]; then
466 |         if [[ ! "${setting}" =~ ^[0-9]+.*$ ]]; then
467 |             add_env_setting_to_conf "${setting}" "${value}" "${temp_neo4j_home}"
468 |         else
469 |             echo >&2 "WARNING: ${setting} not written to conf file because settings that start with a number are not permitted"
470 |         fi
471 |     fi
472 | done
473 | export NEO4J_HOME="${temp_neo4j_home}"
474 | unset temp_neo4j_home
475 | 
476 | # ==== SET PASSWORD AND PLUGINS ====
477 | 
478 | set_initial_password "${temp_neo4j_auth}"
479 | 
480 | 
481 | if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then
482 |   # NEO4JLABS_PLUGINS should be a json array of plugins like '["graph-algorithms", "apoc", "streams", "graphql"]'
483 |   install_neo4j_labs_plugins
484 | fi
485 | 
486 | # ==== INVOKE NEO4J STARTUP ====
487 | 
488 | [ -f "${EXTENSION_SCRIPT:-}" ] && . ${EXTENSION_SCRIPT}
489 | 
490 | if [ "${cmd}" == "dump-config" ]; then
491 |     if ! is_writable "/conf"; then
492 |         print_permissions_advice_and_fail "/conf"
493 |     fi
494 |     cp --recursive "${NEO4J_HOME}"/conf/* /conf
495 |     echo "Config Dumped"
496 |     exit 0
497 | fi
498 | 
499 | # Use su-exec to drop privileges to neo4j user
500 | # Note that su-exec, despite its name, does not replicate the
501 | # functionality of exec, so we need to use both
502 | if [ "${cmd}" == "neo4j" ]; then
503 |   ${exec_cmd} neo4j console
504 | else
505 |   ${exec_cmd} "$@"
506 | fi
507 | 
```

--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/4.1/coredb/docker-entrypoint.sh:
--------------------------------------------------------------------------------

```bash
  1 | #!/bin/bash -eu
  2 | 
  3 | cmd="$1"
  4 | 
  5 | function running_as_root
  6 | {
  7 |     test "$(id -u)" = "0"
  8 | }
  9 | 
 10 | function secure_mode_enabled
 11 | {
 12 |     test "${SECURE_FILE_PERMISSIONS:=no}" = "yes"
 13 | }
 14 | 
 15 | function containsElement
 16 | {
 17 |   local e match="$1"
 18 |   shift
 19 |   for e; do [[ "$e" == "$match" ]] && return 0; done
 20 |   return 1
 21 | }
 22 | 
 23 | function is_readable
 24 | {
 25 |     # this code is fairly ugly but works no matter who this script is running as.
 26 |     # It would be nice if the writability tests could use this logic somehow.
 27 |     local _file=${1}
 28 |     perm=$(stat -c %a "${_file}")
 29 | 
 30 |     # everyone permission
 31 |     if [[ ${perm:2:1} -ge 4 ]]; then
 32 |         return 0
 33 |     fi
 34 |     # owner permissions
 35 |     if [[ ${perm:0:1} -ge 4 ]]; then
 36 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 37 |             return 0
 38 |         fi
 39 |     fi
 40 |     # group permissions
 41 |     if [[ ${perm:1:1} -ge 4 ]]; then
 42 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 43 |             return 0
 44 |         fi
 45 |     fi
 46 |     return 1
 47 | }
 48 | 
 49 | function is_writable
 50 | {
 51 |     # It would be nice if this and the is_readable function could combine somehow
 52 |     local _file=${1}
 53 |     perm=$(stat -c %a "${_file}")
 54 | 
 55 |     # everyone permission
 56 |     if containsElement ${perm:2:1} 2 3 6 7; then
 57 |         return 0
 58 |     fi
 59 |     # owner permissions
 60 |     if containsElement ${perm:0:1} 2 3 6 7; then
 61 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 62 |             return 0
 63 |         fi
 64 |     fi
 65 |     # group permissions
 66 |     if containsElement ${perm:1:1} 2 3 6 7; then
 67 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 68 |             return 0
 69 |         fi
 70 |     fi
 71 |     return 1
 72 | }
 73 | 
 74 | 
 75 | function print_permissions_advice_and_fail
 76 | {
 77 |     _directory=${1}
 78 |     echo >&2 "
 79 | Folder ${_directory} is not accessible for user: ${userid} or group ${groupid} or groups ${groups[@]}, this is commonly a file permissions issue on the mounted folder.
 80 | 
 81 | Hints to solve the issue:
 82 | 1) Make sure the folder exists before mounting it. Docker will create the folder using root permissions before starting the Neo4j container. The root permissions disallow Neo4j from writing to the mounted folder.
 83 | 2) Pass the folder owner's user ID and group ID to docker run, so that docker runs as that user.
 84 | If the folder is owned by the current user, this can be done by adding this flag to your docker run command:
 85 |   --user=\$(id -u):\$(id -g)
 86 |        "
 87 |     exit 1
 88 | }
 89 | 
 90 | function check_mounted_folder_readable
 91 | {
 92 |     local _directory=${1}
 93 |     if ! is_readable "${_directory}"; then
 94 |         print_permissions_advice_and_fail "${_directory}"
 95 |     fi
 96 | }
 97 | 
 98 | function check_mounted_folder_writable_with_chown
 99 | {
100 | # The /data and /log directory are a bit different because they are very likely to be mounted by the user but not
101 | # necessarily writable.
102 | # This depends on whether a user ID is passed to the container and which folders are mounted.
103 | #
104 | #   No user ID passed to container:
105 | #   1) No folders are mounted.
106 | #      The /data and /log folder are owned by neo4j by default, so should be writable already.
107 | #   2) Both /log and /data are mounted.
108 | #      This means on start up, /data and /logs are owned by an unknown user and we should chown them to neo4j for
109 | #      backwards compatibility.
110 | #
111 | #   User ID passed to container:
112 | #   1) Both /data and /logs are mounted
113 | #      The /data and /logs folders are owned by an unknown user but we *should* have rw permission to them.
114 | #      That should be verified and error (helpfully) if not.
115 | #   2) User mounts /data or /logs *but not both*
116 | #      The  unmounted folder is still owned by neo4j, which should already be writable. The mounted folder should
117 | #      have rw permissions through user id. This should be verified.
118 | #   3) No folders are mounted.
119 | #      The /data and /log folder are owned by neo4j by default, and these are already writable by the user.
120 | #      (This is a very unlikely use case).
121 | 
122 |     local mountFolder=${1}
123 |     if running_as_root && ! secure_mode_enabled; then
124 |         # check folder permissions
125 |         if ! is_writable "${mountFolder}" ;  then
126 |             # warn that we're about to chown the folder and then chown it
127 |             echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
128 |             chown -R "${userid}":"${groupid}" "${mountFolder}"
129 |         # check permissions on files in the folder
130 |         elif [ $(gosu "${userid}":"${groupid}" find "${mountFolder}" -not -writable | wc -l) -gt 0 ]; then
131 |             echo "Warning: Some files inside \"${mountFolder}\" are not writable from inside container. Changing folder owner to ${userid}."
132 |             chown -R "${userid}":"${groupid}" "${mountFolder}"
133 |         fi
134 |     else
135 |         if ! is_writable "${mountFolder}"; then
136 |         #if [[ ! -w "${mountFolder}" ]]  && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
137 |             echo >&2 "Consider unsetting SECURE_FILE_PERMISSIONS environment variable, to enable docker to write to ${mountFolder}."
138 |             print_permissions_advice_and_fail "${mountFolder}"
139 |         fi
140 |     fi
141 | }
142 | 
143 | function load_plugin_from_github
144 | {
145 |   # Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
146 |   # correct format.
147 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
148 | 
149 |   local _plugins_dir="${NEO4J_HOME}/plugins"
150 |   if [ -d /plugins ]; then
151 |     local _plugins_dir="/plugins"
152 |   fi
153 |   local _versions_json_url="$(jq --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.versions" /startup/neo4j-plugins.json )"
154 |   # Using the same name for the plugin irrespective of version ensures we don't end up with different versions of the same plugin
155 |   local _destination="${_plugins_dir}/${_plugin_name}.jar"
156 |   local _neo4j_version="$(neo4j --version | cut -d' ' -f2)"
157 | 
158 |   # Now we call out to github to get the versions.json for this plugin and we parse that to find the url for the correct plugin jar for our neo4j version
159 |   echo "Fetching versions.json for Plugin '${_plugin_name}' from ${_versions_json_url}"
160 |   local _versions_json="$(wget -q --timeout 300 --tries 30 -O - "${_versions_json_url}")"
161 |   local _plugin_jar_url="$(echo "${_versions_json}" | jq -L/startup --raw-output "import \"semver\" as lib; [ .[] | select(.neo4j|lib::semver(\"${_neo4j_version}\")) ] | min_by(.neo4j) | .jar")"
162 |   if [[ -z "${_plugin_jar_url}" ]]; then
163 |     echo >&2 "Error: No jar URL found for version '${_neo4j_version}' in versions.json from '${_versions_json_url}'"
164 |     exit 1
165 |   fi
166 |   echo "Installing Plugin '${_plugin_name}' from ${_plugin_jar_url} to ${_destination} "
167 |   wget -q --timeout 300 --tries 30 --output-document="${_destination}" "${_plugin_jar_url}"
168 | 
169 |   if ! is_readable "${_destination}"; then
170 |     echo >&2 "Plugin at '${_destination}' is not readable"
171 |     exit 1
172 |   fi
173 | }
174 | 
175 | function apply_plugin_default_configuration
176 | {
177 |   # Set the correct Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
178 |   # correct format.
179 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
180 |   local _reference_conf="${2}" # used to determine if we can override properties
181 |   local _neo4j_conf="${NEO4J_HOME}/conf/neo4j.conf"
182 | 
183 |   local _property _value
184 |   echo "Applying default values for plugin ${_plugin_name} to neo4j.conf"
185 |   for _entry in $(jq  --compact-output --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.properties | to_entries[]" /startup/neo4j-plugins.json); do
186 |     _property="$(jq --raw-output '.key' <<< "${_entry}")"
187 |     _value="$(jq --raw-output '.value' <<< "${_entry}")"
188 | 
189 |     # the first grep strips out comments
190 |     if grep -o "^[^#]*" "${_reference_conf}" | grep -q --fixed-strings "${_property}=" ; then
191 |       # property is already set in the user provided config. In this case we don't override what has been set explicitly by the user.
192 |       echo "Skipping ${_property} for plugin ${_plugin_name} because it is already set"
193 |     else
194 |       if grep -o "^[^#]*" "${_neo4j_conf}" | grep -q --fixed-strings "${_property}=" ; then
195 |         sed --in-place "s/${_property}=/&${_value},/" "${_neo4j_conf}"
196 |       else
197 |         echo "${_property}=${_value}" >> "${_neo4j_conf}"
198 |       fi
199 |     fi
200 |   done
201 | }
202 | 
203 | function install_neo4j_labs_plugins
204 | {
205 |   # We store a copy of the config before we modify it for the plugins to allow us to see if there are user-set values in the input config that we shouldn't override
206 |   local _old_config="$(mktemp)"
207 |   cp "${NEO4J_HOME}"/conf/neo4j.conf "${_old_config}"
208 |   for plugin_name in $(echo "${NEO4JLABS_PLUGINS}" | jq --raw-output '.[]'); do
209 |     load_plugin_from_github "${plugin_name}"
210 |     apply_plugin_default_configuration "${plugin_name}" "${_old_config}"
211 |   done
212 |   rm "${_old_config}"
213 | }
214 | 
215 | function add_docker_default_to_conf
216 | {
217 |     # docker defaults should NOT overwrite values already in the conf file
218 |     local _setting="${1}"
219 |     local _value="${2}"
220 |     local _neo4j_home="${3}"
221 | 
222 |     if ! grep -q "^${_setting}=" "${_neo4j_home}"/conf/neo4j.conf
223 |     then
224 |         echo -e "\n"${_setting}=${_value} >> "${_neo4j_home}"/conf/neo4j.conf
225 |     fi
226 | }
227 | 
228 | function add_env_setting_to_conf
229 | {
230 |     # settings from environment variables should overwrite values already in the conf
231 |     local _setting=${1}
232 |     local _value=${2}
233 |     local _neo4j_home=${3}
234 | 
235 |     if grep -q -F "${_setting}=" "${_neo4j_home}"/conf/neo4j.conf; then
236 |         # Remove any lines containing the setting already
237 |         sed --in-place "/^${_setting}=.*/d" "${_neo4j_home}"/conf/neo4j.conf
238 |     fi
239 |     # Then always append setting to file
240 |     echo "${_setting}=${_value}" >> "${_neo4j_home}"/conf/neo4j.conf
241 | }
242 | 
243 | function set_initial_password
244 | {
245 |     local _neo4j_auth="${1}"
246 | 
247 |     # set the neo4j initial password only if you run the database server
248 |     if [ "${cmd}" == "neo4j" ]; then
249 |         if [ "${_neo4j_auth:-}" == "none" ]; then
250 |             add_env_setting_to_conf "dbms.security.auth_enabled" "false" "${NEO4J_HOME}"
251 |             # NEO4J_dbms_security_auth__enabled=false
252 |         elif [[ "${_neo4j_auth:-}" =~ ^([^/]+)\/([^/]+)/?([tT][rR][uU][eE])?$ ]]; then
253 |             admin_user="${BASH_REMATCH[1]}"
254 |             password="${BASH_REMATCH[2]}"
255 |             do_reset="${BASH_REMATCH[3]}"
256 | 
257 |             if [ "${password}" == "neo4j" ]; then
258 |                 echo >&2 "Invalid value for password. It cannot be 'neo4j', which is the default."
259 |                 exit 1
260 |             fi
261 |             if [ "${admin_user}" != "neo4j" ]; then
262 |                 echo >&2 "Invalid admin username, it must be neo4j"
263 |                 exit 1
264 |             fi
265 | 
266 |             if running_as_root; then
267 |                 # running set-initial-password as root will create subfolders to /data as root, causing startup fail when neo4j can't read or write the /data/dbms folder
268 |                 # creating the folder first will avoid that
269 |                 mkdir -p /data/dbms
270 |                 chown "${userid}":"${groupid}" /data/dbms
271 |             fi
272 | 
273 |             # Will exit with error if users already exist (and print a message explaining that)
274 |             # we probably don't want the message though, since it throws an error message on restarting the container.
275 |             if [ "${do_reset}" == "true" ]; then
276 |                 neo4j-admin set-initial-password "${password}" --require-password-change 2>/dev/null || true
277 |             else
278 |                 neo4j-admin set-initial-password "${password}" 2>/dev/null || true
279 |             fi
280 |         elif [ -n "${_neo4j_auth:-}" ]; then
281 |             echo "$_neo4j_auth is invalid"
282 |             echo >&2 "Invalid value for NEO4J_AUTH: '${_neo4j_auth}'"
283 |             exit 1
284 |         fi
285 |     fi
286 | }
287 | 
288 | # If we're running as root, then run as the neo4j user. Otherwise
289 | # docker is running with --user and we simply use that user.  Note
290 | # that su-exec, despite its name, does not replicate the functionality
291 | # of exec, so we need to use both
292 | if running_as_root; then
293 |   userid="neo4j"
294 |   groupid="neo4j"
295 |   groups=($(id -G neo4j))
296 |   exec_cmd="exec gosu neo4j:neo4j"
297 | else
298 |   userid="$(id -u)"
299 |   groupid="$(id -g)"
300 |   groups=($(id -G))
301 |   exec_cmd="exec"
302 | fi
303 | readonly userid
304 | readonly groupid
305 | readonly groups
306 | readonly exec_cmd
307 | 
308 | 
309 | # Need to chown the home directory
310 | if running_as_root; then
311 |     chown -R "${userid}":"${groupid}" "${NEO4J_HOME}"
312 |     chmod 700 "${NEO4J_HOME}"
313 |     find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -type d -exec chmod -R 700 {} \;
314 | fi
315 | 
316 | # Only prompt for license agreement if command contains "neo4j" in it
317 | # ==== CHECK LICENSE AGREEMENT ====
318 | 
319 | if [[ "${cmd}" == *"neo4j"* ]]; then
320 |   if [ "${NEO4J_EDITION}" == "enterprise" ]; then
321 |     if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then
322 |       echo >&2 "
323 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
324 | 
325 | (c) Neo4j Sweden AB. 2021.  All Rights Reserved.
326 | Use of this Software without a proper commercial license with Neo4j,
327 | Inc. or its affiliates is prohibited.
328 | 
329 | Email inquiries can be directed to: [email protected]
330 | 
331 | More information is also available at: https://neo4j.com/licensing/
332 | 
333 | 
334 | To accept the license agreement set the environment variable
335 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
336 | 
337 | To do this you can use the following docker argument:
338 | 
339 |         --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
340 | "
341 |       exit 1
342 |     fi
343 |   fi
344 | fi
345 | 
346 | # Env variable naming convention:
347 | # ==== RENAME LEGACY ENVIRONMENT CONF VARIABLES ====
348 | 
349 | # - prefix NEO4J_
350 | # - double underscore char '__' instead of single underscore '_' char in the setting name
351 | # - underscore char '_' instead of dot '.' char in the setting name
352 | # Example:
353 | # NEO4J_dbms_tx__log_rotation_retention__policy env variable to set
354 | #       dbms.tx_log.rotation.retention_policy setting
355 | 
356 | # Backward compatibility - map old hardcoded env variables into new naming convention (if they aren't set already)
357 | # Set some to default values if unset
358 | : ${NEO4J_dbms_tx__log_rotation_retention__policy:=${NEO4J_dbms_txLog_rotation_retentionPolicy:-}}
359 | : ${NEO4J_dbms_unmanaged__extension__classes:=${NEO4J_dbms_unmanagedExtensionClasses:-}}
360 | : ${NEO4J_dbms_allow__format__migration:=${NEO4J_dbms_allowFormatMigration:-}}
361 | : ${NEO4J_dbms_connectors_default__advertised__address:=${NEO4J_dbms_connectors_defaultAdvertisedAddress:-}}
362 | 
363 | if [ "${NEO4J_EDITION}" == "enterprise" ];
364 |   then
365 |    : ${NEO4J_causal__clustering_expected__core__cluster__size:=${NEO4J_causalClustering_expectedCoreClusterSize:-}}
366 |    : ${NEO4J_causal__clustering_initial__discovery__members:=${NEO4J_causalClustering_initialDiscoveryMembers:-}}
367 |    : ${NEO4J_causal__clustering_discovery__advertised__address:=${NEO4J_causalClustering_discoveryAdvertisedAddress:-}}
368 |    : ${NEO4J_causal__clustering_transaction__advertised__address:=${NEO4J_causalClustering_transactionAdvertisedAddress:-}}
369 |    : ${NEO4J_causal__clustering_raft__advertised__address:=${NEO4J_causalClustering_raftAdvertisedAddress:-}}
370 | fi
371 | 
372 | # unset old hardcoded unsupported env variables
373 | unset NEO4J_dbms_txLog_rotation_retentionPolicy NEO4J_UDC_SOURCE \
374 |     NEO4J_dbms_unmanagedExtensionClasses NEO4J_dbms_allowFormatMigration \
375 |     NEO4J_dbms_connectors_defaultAdvertisedAddress NEO4J_ha_serverId \
376 |     NEO4J_ha_initialHosts NEO4J_causalClustering_expectedCoreClusterSize \
377 |     NEO4J_causalClustering_initialDiscoveryMembers \
378 |     NEO4J_causalClustering_discoveryListenAddress \
379 |     NEO4J_causalClustering_discoveryAdvertisedAddress \
380 |     NEO4J_causalClustering_transactionListenAddress \
381 |     NEO4J_causalClustering_transactionAdvertisedAddress \
382 |     NEO4J_causalClustering_raftListenAddress \
383 |     NEO4J_causalClustering_raftAdvertisedAddress
384 | 
385 | 
386 | # ==== CHECK FILE PERMISSIONS ON MOUNTED FOLDERS ====
387 | 
388 | if [ -d /conf ]; then
389 |     check_mounted_folder_readable "/conf"
390 |     find /conf -type f -exec cp {} "${NEO4J_HOME}"/conf \;
391 | fi
392 | 
393 | if [ -d /ssl ]; then
394 |     check_mounted_folder_readable "/ssl"
395 |     rm -rf "${NEO4J_HOME}"/certificates
396 |     ln -s /ssl "${NEO4J_HOME}"/certificates
397 | fi
398 | 
399 | if [ -d /plugins ]; then
400 |     if [[ -n "${NEO4JLABS_PLUGINS:-}" ]]; then
401 |         # We need write permissions
402 |         check_mounted_folder_writable_with_chown "/plugins"
403 |     fi
404 |     check_mounted_folder_readable "/plugins"
405 |     : ${NEO4J_dbms_directories_plugins:="/plugins"}
406 | fi
407 | 
408 | if [ -d /import ]; then
409 |     check_mounted_folder_readable "/import"
410 |     : ${NEO4J_dbms_directories_import:="/import"}
411 | fi
412 | 
413 | if [ -d /metrics ]; then
414 |     # metrics is enterprise only
415 |     if [ "${NEO4J_EDITION}" == "enterprise" ];
416 |     then
417 |         check_mounted_folder_writable_with_chown "/metrics"
418 |         : ${NEO4J_dbms_directories_metrics:="/metrics"}
419 |     fi
420 | fi
421 | 
422 | if [ -d /logs ]; then
423 |     check_mounted_folder_writable_with_chown "/logs"
424 |     : ${NEO4J_dbms_directories_logs:="/logs"}
425 | fi
426 | 
427 | if [ -d /data ]; then
428 |     check_mounted_folder_writable_with_chown "/data"
429 |     if [ -d /data/databases ]; then
430 |         check_mounted_folder_writable_with_chown "/data/databases"
431 |     fi
432 |     if [ -d /data/dbms ]; then
433 |         check_mounted_folder_writable_with_chown "/data/dbms"
434 |     fi
435 |     if [ -d /data/transactions ]; then
436 |         check_mounted_folder_writable_with_chown "/data/transactions"
437 |     fi
438 | fi
439 | 
440 | # ==== SET CONFIGURATIONS ====
441 | 
442 | ## == DOCKER SPECIFIC DEFAULT CONFIGURATIONS ===
443 | ## these should not override *any* configurations set by the user
444 | 
445 | add_docker_default_to_conf "dbms.tx_log.rotation.retention_policy" "100M size" "${NEO4J_HOME}"
446 | add_docker_default_to_conf "dbms.memory.pagecache.size" "512M" "${NEO4J_HOME}"
447 | add_docker_default_to_conf "dbms.default_listen_address" "0.0.0.0" "${NEO4J_HOME}"
448 | # set enterprise only docker defaults
449 | if [ "${NEO4J_EDITION}" == "enterprise" ];
450 | then
451 |     add_docker_default_to_conf "causal_clustering.discovery_advertised_address" "$(hostname):5000" "${NEO4J_HOME}"
452 |     add_docker_default_to_conf "causal_clustering.transaction_advertised_address" "$(hostname):6000" "${NEO4J_HOME}"
453 |     add_docker_default_to_conf "causal_clustering.raft_advertised_address" "$(hostname):7000" "${NEO4J_HOME}"
454 | fi
455 | 
456 | ## == ENVIRONMENT VARIABLE CONFIGURATIONS ===
457 | ## these override BOTH defaults and any existing values in the neo4j.conf file
458 | 
459 | # save NEO4J_HOME and NEO4J_AUTH to temp variables that don't begin with NEO4J_ so they don't get added to the conf
460 | temp_neo4j_home="${NEO4J_HOME}"
461 | temp_neo4j_auth="${NEO4J_AUTH:-}"
462 | # list env variables with prefix NEO4J_ and create settings from them
463 | unset NEO4J_AUTH NEO4J_SHA256 NEO4J_TARBALL NEO4J_EDITION NEO4J_ACCEPT_LICENSE_AGREEMENT NEO4J_HOME
464 | for i in $( set | grep ^NEO4J_ | awk -F'=' '{print $1}' | sort -rn ); do
465 |     setting=$(echo "${i}" | sed 's|^NEO4J_||' | sed 's|_|.|g' | sed 's|\.\.|_|g')
466 |     value=$(echo "${!i}")
467 |     # Don't allow settings with no value or settings that start with a number (neo4j converts settings to env variables and you cannot have an env variable that starts with a number)
468 |     if [[ -n ${value} ]]; then
469 |         if [[ ! "${setting}" =~ ^[0-9]+.*$ ]]; then
470 |             add_env_setting_to_conf "${setting}" "${value}" "${temp_neo4j_home}"
471 |         else
472 |             echo >&2 "WARNING: ${setting} not written to conf file because settings that start with a number are not permitted"
473 |         fi
474 |     fi
475 | done
476 | export NEO4J_HOME="${temp_neo4j_home}"
477 | unset temp_neo4j_home
478 | 
479 | # ==== SET PASSWORD AND PLUGINS ====
480 | 
481 | set_initial_password "${temp_neo4j_auth}"
482 | 
483 | 
484 | if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then
485 |   # NEO4JLABS_PLUGINS should be a json array of plugins like '["graph-algorithms", "apoc", "streams", "graphql"]'
486 |   install_neo4j_labs_plugins
487 | fi
488 | 
489 | # ==== INVOKE NEO4J STARTUP ====
490 | 
491 | [ -f "${EXTENSION_SCRIPT:-}" ] && . ${EXTENSION_SCRIPT}
492 | 
493 | if [ "${cmd}" == "dump-config" ]; then
494 |     if [ ! -d "/conf" ]; then
495 |         echo >&2 "You must mount a folder to /conf so that the configuration file(s) can be dumped to there."
496 |         exit 1
497 |     fi
498 |     check_mounted_folder_writable_with_chown "/conf"
499 |     cp --recursive "${NEO4J_HOME}"/conf/* /conf
500 |     echo "Config Dumped"
501 |     exit 0
502 | fi
503 | 
504 | # Use su-exec to drop privileges to neo4j user
505 | # Note that su-exec, despite its name, does not replicate the
506 | # functionality of exec, so we need to use both
507 | if [ "${cmd}" == "neo4j" ]; then
508 |   ${exec_cmd} neo4j console
509 | else
510 |   ${exec_cmd} "$@"
511 | fi
512 | 
```

--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/4.2/coredb/docker-entrypoint.sh:
--------------------------------------------------------------------------------

```bash
  1 | #!/bin/bash -eu
  2 | 
  3 | cmd="$1"
  4 | 
  5 | function running_as_root
  6 | {
  7 |     test "$(id -u)" = "0"
  8 | }
  9 | 
 10 | function secure_mode_enabled
 11 | {
 12 |     test "${SECURE_FILE_PERMISSIONS:=no}" = "yes"
 13 | }
 14 | 
 15 | function containsElement
 16 | {
 17 |   local e match="$1"
 18 |   shift
 19 |   for e; do [[ "$e" == "$match" ]] && return 0; done
 20 |   return 1
 21 | }
 22 | 
 23 | function is_readable
 24 | {
 25 |     # this code is fairly ugly but works no matter who this script is running as.
 26 |     # It would be nice if the writability tests could use this logic somehow.
 27 |     local _file=${1}
 28 |     perm=$(stat -c %a "${_file}")
 29 | 
 30 |     # everyone permission
 31 |     if [[ ${perm:2:1} -ge 4 ]]; then
 32 |         return 0
 33 |     fi
 34 |     # owner permissions
 35 |     if [[ ${perm:0:1} -ge 4 ]]; then
 36 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 37 |             return 0
 38 |         fi
 39 |     fi
 40 |     # group permissions
 41 |     if [[ ${perm:1:1} -ge 4 ]]; then
 42 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 43 |             return 0
 44 |         fi
 45 |     fi
 46 |     return 1
 47 | }
 48 | 
 49 | function is_writable
 50 | {
 51 |     # It would be nice if this and the is_readable function could combine somehow
 52 |     local _file=${1}
 53 |     perm=$(stat -c %a "${_file}")
 54 | 
 55 |     # everyone permission
 56 |     if containsElement ${perm:2:1} 2 3 6 7; then
 57 |         return 0
 58 |     fi
 59 |     # owner permissions
 60 |     if containsElement ${perm:0:1} 2 3 6 7; then
 61 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 62 |             return 0
 63 |         fi
 64 |     fi
 65 |     # group permissions
 66 |     if containsElement ${perm:1:1} 2 3 6 7; then
 67 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 68 |             return 0
 69 |         fi
 70 |     fi
 71 |     return 1
 72 | }
 73 | 
 74 | function expand_commands_optionally
 75 | {
 76 |     if [ "${EXTENDED_CONF+"yes"}" == "yes" ]; then
 77 |         echo "--expand-commands"
 78 |     fi
 79 | }
 80 | 
 81 | function print_permissions_advice_and_fail
 82 | {
 83 |     _directory=${1}
 84 |     echo >&2 "
 85 | Folder ${_directory} is not accessible for user: ${userid} or group ${groupid} or groups ${groups[@]}, this is commonly a file permissions issue on the mounted folder.
 86 | 
 87 | Hints to solve the issue:
 88 | 1) Make sure the folder exists before mounting it. Docker will create the folder using root permissions before starting the Neo4j container. The root permissions disallow Neo4j from writing to the mounted folder.
 89 | 2) Pass the folder owner's user ID and group ID to docker run, so that docker runs as that user.
 90 | If the folder is owned by the current user, this can be done by adding this flag to your docker run command:
 91 |   --user=\$(id -u):\$(id -g)
 92 |        "
 93 |     exit 1
 94 | }
 95 | 
 96 | function check_mounted_folder_readable
 97 | {
 98 |     local _directory=${1}
 99 |     if ! is_readable "${_directory}"; then
100 |         print_permissions_advice_and_fail "${_directory}"
101 |     fi
102 | }
103 | 
104 | function check_mounted_folder_writable_with_chown
105 | {
106 | # The /data and /log directory are a bit different because they are very likely to be mounted by the user but not
107 | # necessarily writable.
108 | # This depends on whether a user ID is passed to the container and which folders are mounted.
109 | #
110 | #   No user ID passed to container:
111 | #   1) No folders are mounted.
112 | #      The /data and /log folder are owned by neo4j by default, so should be writable already.
113 | #   2) Both /log and /data are mounted.
114 | #      This means on start up, /data and /logs are owned by an unknown user and we should chown them to neo4j for
115 | #      backwards compatibility.
116 | #
117 | #   User ID passed to container:
118 | #   1) Both /data and /logs are mounted
119 | #      The /data and /logs folders are owned by an unknown user but we *should* have rw permission to them.
120 | #      That should be verified and error (helpfully) if not.
121 | #   2) User mounts /data or /logs *but not both*
122 | #      The  unmounted folder is still owned by neo4j, which should already be writable. The mounted folder should
123 | #      have rw permissions through user id. This should be verified.
124 | #   3) No folders are mounted.
125 | #      The /data and /log folder are owned by neo4j by default, and these are already writable by the user.
126 | #      (This is a very unlikely use case).
127 | 
128 |     local mountFolder=${1}
129 |     if running_as_root && ! secure_mode_enabled; then
130 |         # check folder permissions
131 |         if ! is_writable "${mountFolder}" ;  then
132 |             # warn that we're about to chown the folder and then chown it
133 |             echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
134 |             chown -R "${userid}":"${groupid}" "${mountFolder}"
135 |         # check permissions on files in the folder
136 |         elif [ $(gosu "${userid}":"${groupid}" find "${mountFolder}" -not -writable | wc -l) -gt 0 ]; then
137 |             echo "Warning: Some files inside \"${mountFolder}\" are not writable from inside container. Changing folder owner to ${userid}."
138 |             chown -R "${userid}":"${groupid}" "${mountFolder}"
139 |         fi
140 |     else
141 |         if ! is_writable "${mountFolder}"; then
142 |         #if [[ ! -w "${mountFolder}" ]]  && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
143 |             echo >&2 "Consider unsetting SECURE_FILE_PERMISSIONS environment variable, to enable docker to write to ${mountFolder}."
144 |             print_permissions_advice_and_fail "${mountFolder}"
145 |         fi
146 |     fi
147 | }
148 | 
149 | function load_plugin_from_github
150 | {
151 |   # Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
152 |   # correct format.
153 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
154 | 
155 |   local _plugins_dir="${NEO4J_HOME}/plugins"
156 |   if [ -d /plugins ]; then
157 |     local _plugins_dir="/plugins"
158 |   fi
159 |   local _versions_json_url="$(jq --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.versions" /startup/neo4j-plugins.json )"
160 |   # Using the same name for the plugin irrespective of version ensures we don't end up with different versions of the same plugin
161 |   local _destination="${_plugins_dir}/${_plugin_name}.jar"
162 |   local _neo4j_version="$(neo4j --version | cut -d' ' -f2)"
163 | 
164 |   # Now we call out to github to get the versions.json for this plugin and we parse that to find the url for the correct plugin jar for our neo4j version
165 |   echo "Fetching versions.json for Plugin '${_plugin_name}' from ${_versions_json_url}"
166 |   local _versions_json="$(wget -q --timeout 300 --tries 30 -O - "${_versions_json_url}")"
167 |   local _plugin_jar_url="$(echo "${_versions_json}" | jq -L/startup --raw-output "import \"semver\" as lib; [ .[] | select(.neo4j|lib::semver(\"${_neo4j_version}\")) ] | min_by(.neo4j) | .jar")"
168 |   if [[ -z "${_plugin_jar_url}" ]]; then
169 |     echo >&2 "Error: No jar URL found for version '${_neo4j_version}' in versions.json from '${_versions_json_url}'"
170 |     exit 1
171 |   fi
172 |   echo "Installing Plugin '${_plugin_name}' from ${_plugin_jar_url} to ${_destination} "
173 |   wget -q --timeout 300 --tries 30 --output-document="${_destination}" "${_plugin_jar_url}"
174 | 
175 |   if ! is_readable "${_destination}"; then
176 |     echo >&2 "Plugin at '${_destination}' is not readable"
177 |     exit 1
178 |   fi
179 | }
180 | 
181 | function apply_plugin_default_configuration
182 | {
183 |   # Set the correct Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
184 |   # correct format.
185 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
186 |   local _reference_conf="${2}" # used to determine if we can override properties
187 |   local _neo4j_conf="${NEO4J_HOME}/conf/neo4j.conf"
188 | 
189 |   local _property _value
190 |   echo "Applying default values for plugin ${_plugin_name} to neo4j.conf"
191 |   for _entry in $(jq  --compact-output --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.properties | to_entries[]" /startup/neo4j-plugins.json); do
192 |     _property="$(jq --raw-output '.key' <<< "${_entry}")"
193 |     _value="$(jq --raw-output '.value' <<< "${_entry}")"
194 | 
195 |     # the first grep strips out comments
196 |     if grep -o "^[^#]*" "${_reference_conf}" | grep -q --fixed-strings "${_property}=" ; then
197 |       # property is already set in the user provided config. In this case we don't override what has been set explicitly by the user.
198 |       echo "Skipping ${_property} for plugin ${_plugin_name} because it is already set"
199 |     else
200 |       if grep -o "^[^#]*" "${_neo4j_conf}" | grep -q --fixed-strings "${_property}=" ; then
201 |         sed --in-place "s/${_property}=/&${_value},/" "${_neo4j_conf}"
202 |       else
203 |         echo "${_property}=${_value}" >> "${_neo4j_conf}"
204 |       fi
205 |     fi
206 |   done
207 | }
208 | 
209 | function install_neo4j_labs_plugins
210 | {
211 |   # We store a copy of the config before we modify it for the plugins to allow us to see if there are user-set values in the input config that we shouldn't override
212 |   local _old_config="$(mktemp)"
213 |   cp "${NEO4J_HOME}"/conf/neo4j.conf "${_old_config}"
214 |   for plugin_name in $(echo "${NEO4JLABS_PLUGINS}" | jq --raw-output '.[]'); do
215 |     load_plugin_from_github "${plugin_name}"
216 |     apply_plugin_default_configuration "${plugin_name}" "${_old_config}"
217 |   done
218 |   rm "${_old_config}"
219 | }
220 | 
221 | function add_docker_default_to_conf
222 | {
223 |     # docker defaults should NOT overwrite values already in the conf file
224 |     local _setting="${1}"
225 |     local _value="${2}"
226 |     local _neo4j_home="${3}"
227 | 
228 |     if ! grep -q "^${_setting}=" "${_neo4j_home}"/conf/neo4j.conf
229 |     then
230 |         echo -e "\n"${_setting}=${_value} >> "${_neo4j_home}"/conf/neo4j.conf
231 |     fi
232 | }
233 | 
234 | function add_env_setting_to_conf
235 | {
236 |     # settings from environment variables should overwrite values already in the conf
237 |     local _setting=${1}
238 |     local _value=${2}
239 |     local _neo4j_home=${3}
240 | 
241 |     if grep -q -F "${_setting}=" "${_neo4j_home}"/conf/neo4j.conf; then
242 |         # Remove any lines containing the setting already
243 |         sed --in-place "/^${_setting}=.*/d" "${_neo4j_home}"/conf/neo4j.conf
244 |     fi
245 |     # Then always append setting to file
246 |     echo "${_setting}=${_value}" >> "${_neo4j_home}"/conf/neo4j.conf
247 | }
248 | 
249 | function set_initial_password
250 | {
251 |     local _neo4j_auth="${1}"
252 | 
253 |     # set the neo4j initial password only if you run the database server
254 |     if [ "${cmd}" == "neo4j" ]; then
255 |         if [ "${_neo4j_auth:-}" == "none" ]; then
256 |             add_env_setting_to_conf "dbms.security.auth_enabled" "false" "${NEO4J_HOME}"
257 |             # NEO4J_dbms_security_auth__enabled=false
258 |         elif [[ "${_neo4j_auth:-}" =~ ^([^/]+)\/([^/]+)/?([tT][rR][uU][eE])?$ ]]; then
259 |             admin_user="${BASH_REMATCH[1]}"
260 |             password="${BASH_REMATCH[2]}"
261 |             do_reset="${BASH_REMATCH[3]}"
262 | 
263 |             if [ "${password}" == "neo4j" ]; then
264 |                 echo >&2 "Invalid value for password. It cannot be 'neo4j', which is the default."
265 |                 exit 1
266 |             fi
267 |             if [ "${admin_user}" != "neo4j" ]; then
268 |                 echo >&2 "Invalid admin username, it must be neo4j"
269 |                 exit 1
270 |             fi
271 | 
272 |             if running_as_root; then
273 |                 # running set-initial-password as root will create subfolders to /data as root, causing startup fail when neo4j can't read or write the /data/dbms folder
274 |                 # creating the folder first will avoid that
275 |                 mkdir -p /data/dbms
276 |                 chown "${userid}":"${groupid}" /data/dbms
277 |             fi
278 | 
279 |             # Will exit with error if users already exist (and print a message explaining that)
280 |             # we probably don't want the message though, since it throws an error message on restarting the container.
281 |             if [ "${do_reset}" == "true" ]; then
282 |                 ${neo4j_admin_cmd} set-initial-password "${password}" --require-password-change $(expand_commands_optionally) 2>/dev/null || true
283 |             else
284 |                 ${neo4j_admin_cmd} set-initial-password "${password}" $(expand_commands_optionally) 2>/dev/null || true
285 |             fi
286 |         elif [ -n "${_neo4j_auth:-}" ]; then
287 |             echo "$_neo4j_auth is invalid"
288 |             echo >&2 "Invalid value for NEO4J_AUTH: '${_neo4j_auth}'"
289 |             exit 1
290 |         fi
291 |     fi
292 | }
293 | 
294 | # If we're running as root, then run as the neo4j user. Otherwise
295 | # docker is running with --user and we simply use that user.  Note
296 | # that su-exec, despite its name, does not replicate the functionality
297 | # of exec, so we need to use both
298 | if running_as_root; then
299 |   userid="neo4j"
300 |   groupid="neo4j"
301 |   groups=($(id -G neo4j))
302 |   exec_cmd="exec gosu neo4j:neo4j"
303 |   neo4j_admin_cmd="gosu neo4j:neo4j neo4j-admin"
304 | else
305 |   userid="$(id -u)"
306 |   groupid="$(id -g)"
307 |   groups=($(id -G))
308 |   exec_cmd="exec"
309 |   neo4j_admin_cmd="neo4j-admin"
310 | fi
311 | readonly userid
312 | readonly groupid
313 | readonly groups
314 | readonly exec_cmd
315 | readonly neo4j_admin_cmd
316 | 
317 | 
318 | # Need to chown the home directory
319 | if running_as_root; then
320 |     chown -R "${userid}":"${groupid}" "${NEO4J_HOME}"
321 |     chmod 700 "${NEO4J_HOME}"
322 |     find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -type d -exec chmod -R 700 {} \;
323 |     find "${NEO4J_HOME}"/conf -type f -exec chmod -R 600 {} \;
324 | fi
325 | 
326 | # ==== CHECK LICENSE AGREEMENT ====
327 | 
328 | # Only prompt for license agreement if command contains "neo4j" in it
329 | if [[ "${cmd}" == *"neo4j"* ]]; then
330 |   if [ "${NEO4J_EDITION}" == "enterprise" ]; then
331 |     if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then
332 |       echo >&2 "
333 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
334 | 
335 | (c) Neo4j Sweden AB. 2021.  All Rights Reserved.
336 | Use of this Software without a proper commercial license with Neo4j,
337 | Inc. or its affiliates is prohibited.
338 | 
339 | Email inquiries can be directed to: [email protected]
340 | 
341 | More information is also available at: https://neo4j.com/licensing/
342 | 
343 | 
344 | To accept the license agreement set the environment variable
345 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
346 | 
347 | To do this you can use the following docker argument:
348 | 
349 |         --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
350 | "
351 |       exit 1
352 |     fi
353 |   fi
354 | fi
355 | 
356 | # ==== RENAME LEGACY ENVIRONMENT CONF VARIABLES ====
357 | 
358 | # Env variable naming convention:
359 | # - prefix NEO4J_
360 | # - double underscore char '__' instead of single underscore '_' char in the setting name
361 | # - underscore char '_' instead of dot '.' char in the setting name
362 | # Example:
363 | # NEO4J_dbms_tx__log_rotation_retention__policy env variable to set
364 | #       dbms.tx_log.rotation.retention_policy setting
365 | 
366 | # Backward compatibility - map old hardcoded env variables into new naming convention (if they aren't set already)
367 | # Set some to default values if unset
368 | : ${NEO4J_dbms_tx__log_rotation_retention__policy:=${NEO4J_dbms_txLog_rotation_retentionPolicy:-}}
369 | : ${NEO4J_dbms_unmanaged__extension__classes:=${NEO4J_dbms_unmanagedExtensionClasses:-}}
370 | : ${NEO4J_dbms_allow__format__migration:=${NEO4J_dbms_allowFormatMigration:-}}
371 | : ${NEO4J_dbms_connectors_default__advertised__address:=${NEO4J_dbms_connectors_defaultAdvertisedAddress:-}}
372 | 
373 | if [ "${NEO4J_EDITION}" == "enterprise" ];
374 |   then
375 |    : ${NEO4J_causal__clustering_expected__core__cluster__size:=${NEO4J_causalClustering_expectedCoreClusterSize:-}}
376 |    : ${NEO4J_causal__clustering_initial__discovery__members:=${NEO4J_causalClustering_initialDiscoveryMembers:-}}
377 |    : ${NEO4J_causal__clustering_discovery__advertised__address:=${NEO4J_causalClustering_discoveryAdvertisedAddress:-}}
378 |    : ${NEO4J_causal__clustering_transaction__advertised__address:=${NEO4J_causalClustering_transactionAdvertisedAddress:-}}
379 |    : ${NEO4J_causal__clustering_raft__advertised__address:=${NEO4J_causalClustering_raftAdvertisedAddress:-}}
380 | fi
381 | 
382 | # unset old hardcoded unsupported env variables
383 | unset NEO4J_dbms_txLog_rotation_retentionPolicy NEO4J_UDC_SOURCE \
384 |     NEO4J_dbms_unmanagedExtensionClasses NEO4J_dbms_allowFormatMigration \
385 |     NEO4J_dbms_connectors_defaultAdvertisedAddress NEO4J_ha_serverId \
386 |     NEO4J_ha_initialHosts NEO4J_causalClustering_expectedCoreClusterSize \
387 |     NEO4J_causalClustering_initialDiscoveryMembers \
388 |     NEO4J_causalClustering_discoveryListenAddress \
389 |     NEO4J_causalClustering_discoveryAdvertisedAddress \
390 |     NEO4J_causalClustering_transactionListenAddress \
391 |     NEO4J_causalClustering_transactionAdvertisedAddress \
392 |     NEO4J_causalClustering_raftListenAddress \
393 |     NEO4J_causalClustering_raftAdvertisedAddress
394 | 
395 | 
396 | # ==== CHECK FILE PERMISSIONS ON MOUNTED FOLDERS ====
397 | 
398 | if [ -d /conf ]; then
399 |     check_mounted_folder_readable "/conf"
400 |     rm -rf "${NEO4J_HOME}"/conf/*
401 |     find /conf -type f -exec cp --preserve=ownership,mode {} "${NEO4J_HOME}"/conf \;
402 | fi
403 | 
404 | if [ -d /ssl ]; then
405 |     check_mounted_folder_readable "/ssl"
406 |     rm -rf "${NEO4J_HOME}"/certificates
407 |     ln -s /ssl "${NEO4J_HOME}"/certificates
408 | fi
409 | 
410 | if [ -d /plugins ]; then
411 |     if [[ -n "${NEO4JLABS_PLUGINS:-}" ]]; then
412 |         # We need write permissions
413 |         check_mounted_folder_writable_with_chown "/plugins"
414 |     fi
415 |     check_mounted_folder_readable "/plugins"
416 |     : ${NEO4J_dbms_directories_plugins:="/plugins"}
417 | fi
418 | 
419 | if [ -d /import ]; then
420 |     check_mounted_folder_readable "/import"
421 |     : ${NEO4J_dbms_directories_import:="/import"}
422 | fi
423 | 
424 | if [ -d /metrics ]; then
425 |     # metrics is enterprise only
426 |     if [ "${NEO4J_EDITION}" == "enterprise" ];
427 |     then
428 |         check_mounted_folder_writable_with_chown "/metrics"
429 |         : ${NEO4J_dbms_directories_metrics:="/metrics"}
430 |     fi
431 | fi
432 | 
433 | if [ -d /logs ]; then
434 |     check_mounted_folder_writable_with_chown "/logs"
435 |     : ${NEO4J_dbms_directories_logs:="/logs"}
436 | fi
437 | 
438 | if [ -d /data ]; then
439 |     check_mounted_folder_writable_with_chown "/data"
440 |     if [ -d /data/databases ]; then
441 |         check_mounted_folder_writable_with_chown "/data/databases"
442 |     fi
443 |     if [ -d /data/dbms ]; then
444 |         check_mounted_folder_writable_with_chown "/data/dbms"
445 |     fi
446 |     if [ -d /data/transactions ]; then
447 |         check_mounted_folder_writable_with_chown "/data/transactions"
448 |     fi
449 | fi
450 | 
451 | if [ -d /licenses ]; then
452 |     check_mounted_folder_readable "/licenses"
453 |     : ${NEO4J_dbms_directories_licenses:="/licenses"}
454 | fi
455 | 
456 | # ==== SET CONFIGURATIONS ====
457 | 
458 | ## == DOCKER SPECIFIC DEFAULT CONFIGURATIONS ===
459 | ## these should not override *any* configurations set by the user
460 | 
461 | add_docker_default_to_conf "dbms.tx_log.rotation.retention_policy" "100M size" "${NEO4J_HOME}"
462 | add_docker_default_to_conf "dbms.memory.pagecache.size" "512M" "${NEO4J_HOME}"
463 | add_docker_default_to_conf "dbms.default_listen_address" "0.0.0.0" "${NEO4J_HOME}"
464 | # set enterprise only docker defaults
465 | if [ "${NEO4J_EDITION}" == "enterprise" ];
466 | then
467 |     add_docker_default_to_conf "causal_clustering.discovery_advertised_address" "$(hostname):5000" "${NEO4J_HOME}"
468 |     add_docker_default_to_conf "causal_clustering.transaction_advertised_address" "$(hostname):6000" "${NEO4J_HOME}"
469 |     add_docker_default_to_conf "causal_clustering.raft_advertised_address" "$(hostname):7000" "${NEO4J_HOME}"
470 | fi
471 | 
472 | ## == ENVIRONMENT VARIABLE CONFIGURATIONS ===
473 | ## these override BOTH defaults and any existing values in the neo4j.conf file
474 | 
475 | # save NEO4J_HOME and NEO4J_AUTH to temp variables that don't begin with NEO4J_ so they don't get added to the conf
476 | temp_neo4j_home="${NEO4J_HOME}"
477 | temp_neo4j_auth="${NEO4J_AUTH:-}"
478 | # list env variables with prefix NEO4J_ and create settings from them
479 | unset NEO4J_AUTH NEO4J_SHA256 NEO4J_TARBALL NEO4J_EDITION NEO4J_ACCEPT_LICENSE_AGREEMENT NEO4J_HOME
480 | for i in $( set | grep ^NEO4J_ | awk -F'=' '{print $1}' | sort -rn ); do
481 |     setting=$(echo "${i}" | sed 's|^NEO4J_||' | sed 's|_|.|g' | sed 's|\.\.|_|g')
482 |     value=$(echo "${!i}")
483 |     # Don't allow settings with no value or settings that start with a number (neo4j converts settings to env variables and you cannot have an env variable that starts with a number)
484 |     if [[ -n ${value} ]]; then
485 |         if [[ ! "${setting}" =~ ^[0-9]+.*$ ]]; then
486 |             add_env_setting_to_conf "${setting}" "${value}" "${temp_neo4j_home}"
487 |         else
488 |             echo >&2 "WARNING: ${setting} not written to conf file because settings that start with a number are not permitted"
489 |         fi
490 |     fi
491 | done
492 | export NEO4J_HOME="${temp_neo4j_home}"
493 | unset temp_neo4j_home
494 | 
495 | # ==== SET PASSWORD AND PLUGINS ====
496 | 
497 | set_initial_password "${temp_neo4j_auth}"
498 | 
499 | 
500 | if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then
501 |   # NEO4JLABS_PLUGINS should be a json array of plugins like '["graph-algorithms", "apoc", "streams", "graphql"]'
502 |   install_neo4j_labs_plugins
503 | fi
504 | 
505 | # ==== INVOKE NEO4J STARTUP ====
506 | 
507 | [ -f "${EXTENSION_SCRIPT:-}" ] && . ${EXTENSION_SCRIPT}
508 | 
509 | if [ "${cmd}" == "dump-config" ]; then
510 |     if [ ! -d "/conf" ]; then
511 |         echo >&2 "You must mount a folder to /conf so that the configuration file(s) can be dumped to there."
512 |         exit 1
513 |     fi
514 |     check_mounted_folder_writable_with_chown "/conf"
515 |     cp --recursive "${NEO4J_HOME}"/conf/* /conf
516 |     echo "Config Dumped"
517 |     exit 0
518 | fi
519 | 
520 | # Use su-exec to drop privileges to neo4j user
521 | # Note that su-exec, despite its name, does not replicate the
522 | # functionality of exec, so we need to use both
523 | if [ "${cmd}" == "neo4j" ]; then
524 |     if [ "${EXTENDED_CONF+"yes"}" == "yes" ]; then
525 |         ${exec_cmd} neo4j console --expand-commands
526 |     else
527 |         ${exec_cmd} neo4j console
528 |     fi
529 | else
530 |   ${exec_cmd} "$@"
531 | fi
532 | 
```

--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/3.5/coredb/docker-entrypoint.sh:
--------------------------------------------------------------------------------

```bash
  1 | #!/bin/bash -eu
  2 | 
  3 | cmd="$1"
  4 | 
  5 | function running_as_root
  6 | {
  7 |     test "$(id -u)" = "0"
  8 | }
  9 | 
 10 | function secure_mode_enabled
 11 | {
 12 |     test "${SECURE_FILE_PERMISSIONS:=no}" = "yes"
 13 | }
 14 | 
 15 | function containsElement
 16 | {
 17 |   local e match="$1"
 18 |   shift
 19 |   for e; do [[ "$e" == "$match" ]] && return 0; done
 20 |   return 1
 21 | }
 22 | 
 23 | function is_readable
 24 | {
 25 |     # this code is fairly ugly but works no matter who this script is running as.
 26 |     # It would be nice if the writability tests could use this logic somehow.
 27 |     local _file=${1}
 28 |     perm=$(stat -c %a "${_file}")
 29 | 
 30 |     # everyone permission
 31 |     if [[ ${perm:2:1} -ge 4 ]]; then
 32 |         return 0
 33 |     fi
 34 |     # owner permissions
 35 |     if [[ ${perm:0:1} -ge 4 ]]; then
 36 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 37 |             return 0
 38 |         fi
 39 |     fi
 40 |     # group permissions
 41 |     if [[ ${perm:1:1} -ge 4 ]]; then
 42 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 43 |             return 0
 44 |         fi
 45 |     fi
 46 |     return 1
 47 | }
 48 | 
 49 | function is_writable
 50 | {
 51 |     # It would be nice if this and the is_readable function could combine somehow
 52 |     local _file=${1}
 53 |     perm=$(stat -c %a "${_file}")
 54 | 
 55 |     # everyone permission
 56 |     if containsElement ${perm:2:1} 2 3 6 7; then
 57 |         return 0
 58 |     fi
 59 |     # owner permissions
 60 |     if containsElement ${perm:0:1} 2 3 6 7; then
 61 |         if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then
 62 |             return 0
 63 |         fi
 64 |     fi
 65 |     # group permissions
 66 |     if containsElement ${perm:1:1} 2 3 6 7; then
 67 |         if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then
 68 |             return 0
 69 |         fi
 70 |     fi
 71 |     return 1
 72 | }
 73 | 
 74 | 
 75 | function print_permissions_advice_and_fail
 76 | {
 77 |     _directory=${1}
 78 |     echo >&2 "
 79 | Folder ${_directory} is not accessible for user: ${userid} or group ${groupid} or groups ${groups[@]}, this is commonly a file permissions issue on the mounted folder.
 80 | 
 81 | Hints to solve the issue:
 82 | 1) Make sure the folder exists before mounting it. Docker will create the folder using root permissions before starting the Neo4j container. The root permissions disallow Neo4j from writing to the mounted folder.
 83 | 2) Pass the folder owner's user ID and group ID to docker run, so that docker runs as that user.
 84 | If the folder is owned by the current user, this can be done by adding this flag to your docker run command:
 85 |   --user=\$(id -u):\$(id -g)
 86 |        "
 87 |     exit 1
 88 | }
 89 | 
 90 | function check_mounted_folder_readable
 91 | {
 92 |     local _directory=${1}
 93 |     if ! is_readable "${_directory}"; then
 94 |         print_permissions_advice_and_fail "${_directory}"
 95 |     fi
 96 | }
 97 | 
 98 | function check_mounted_folder_writable_with_chown
 99 | {
100 | # The /data and /log directory are a bit different because they are very likely to be mounted by the user but not
101 | # necessarily writable.
102 | # This depends on whether a user ID is passed to the container and which folders are mounted.
103 | #
104 | #   No user ID passed to container:
105 | #   1) No folders are mounted.
106 | #      The /data and /log folder are owned by neo4j by default, so should be writable already.
107 | #   2) Both /log and /data are mounted.
108 | #      This means on start up, /data and /logs are owned by an unknown user and we should chown them to neo4j for
109 | #      backwards compatibility.
110 | #
111 | #   User ID passed to container:
112 | #   1) Both /data and /logs are mounted
113 | #      The /data and /logs folders are owned by an unknown user but we *should* have rw permission to them.
114 | #      That should be verified and error (helpfully) if not.
115 | #   2) User mounts /data or /logs *but not both*
116 | #      The  unmounted folder is still owned by neo4j, which should already be writable. The mounted folder should
117 | #      have rw permissions through user id. This should be verified.
118 | #   3) No folders are mounted.
119 | #      The /data and /log folder are owned by neo4j by default, and these are already writable by the user.
120 | #      (This is a very unlikely use case).
121 | 
122 |     local mountFolder=${1}
123 |     if running_as_root && ! secure_mode_enabled; then
124 |         # check folder permissions
125 |         if ! is_writable "${mountFolder}" ;  then
126 |             # warn that we're about to chown the folder and then chown it
127 |             echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
128 |             chown -R "${userid}":"${groupid}" "${mountFolder}"
129 |         # check permissions on files in the folder
130 |         elif [ $(gosu "${userid}":"${groupid}" find "${mountFolder}" -not -writable | wc -l) -gt 0 ]; then
131 |             echo "Warning: Some files inside \"${mountFolder}\" are not writable from inside container. Changing folder owner to ${userid}."
132 |             chown -R "${userid}":"${groupid}" "${mountFolder}"
133 |         fi
134 |     else
135 |         if ! is_writable "${mountFolder}"; then
136 |         #if [[ ! -w "${mountFolder}" ]]  && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
137 |             echo >&2 "Consider unsetting SECURE_FILE_PERMISSIONS environment variable, to enable docker to write to ${mountFolder}."
138 |             print_permissions_advice_and_fail "${mountFolder}"
139 |         fi
140 |     fi
141 | }
142 | 
143 | function load_plugin_from_github
144 | {
145 |   # Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
146 |   # correct format.
147 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
148 | 
149 |   local _plugins_dir="${NEO4J_HOME}/plugins"
150 |   if [ -d /plugins ]; then
151 |     local _plugins_dir="/plugins"
152 |   fi
153 |   local _versions_json_url="$(jq --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.versions" /startup/neo4j-plugins.json )"
154 |   # Using the same name for the plugin irrespective of version ensures we don't end up with different versions of the same plugin
155 |   local _destination="${_plugins_dir}/${_plugin_name}.jar"
156 |   local _neo4j_version="$(neo4j --version | cut -d' ' -f2)"
157 | 
158 |   # Now we call out to github to get the versions.json for this plugin and we parse that to find the url for the correct plugin jar for our neo4j version
159 |   echo "Fetching versions.json for Plugin '${_plugin_name}' from ${_versions_json_url}"
160 |   local _versions_json="$(wget -q --timeout 300 --tries 30 -O - "${_versions_json_url}")"
161 |   local _plugin_jar_url="$(echo "${_versions_json}" | jq -L/startup --raw-output "import \"semver\" as lib; [ .[] | select(.neo4j|lib::semver(\"${_neo4j_version}\")) ] | min_by(.neo4j) | .jar")"
162 |   if [[ -z "${_plugin_jar_url}" ]]; then
163 |     echo >&2 "Error: No jar URL found for version '${_neo4j_version}' in versions.json from '${_versions_json_url}'"
164 |     exit 1
165 |   fi
166 |   echo "Installing Plugin '${_plugin_name}' from ${_plugin_jar_url} to ${_destination} "
167 |   wget -q --timeout 300 --tries 30 --output-document="${_destination}" "${_plugin_jar_url}"
168 | 
169 |   if ! is_readable "${_destination}"; then
170 |     echo >&2 "Plugin at '${_destination}' is not readable"
171 |     exit 1
172 |   fi
173 | }
174 | 
175 | function apply_plugin_default_configuration
176 | {
177 |   # Set the correct Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the
178 |   # correct format.
179 |   local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql
180 |   local _reference_conf="${2}" # used to determine if we can override properties
181 |   local _neo4j_conf="${NEO4J_HOME}/conf/neo4j.conf"
182 | 
183 |   local _property _value
184 |   echo "Applying default values for plugin ${_plugin_name} to neo4j.conf"
185 |   for _entry in $(jq  --compact-output --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value.properties | to_entries[]" /startup/neo4j-plugins.json); do
186 |     _property="$(jq --raw-output '.key' <<< "${_entry}")"
187 |     _value="$(jq --raw-output '.value' <<< "${_entry}")"
188 | 
189 |     # the first grep strips out comments
190 |     if grep -o "^[^#]*" "${_reference_conf}" | grep -q --fixed-strings "${_property}=" ; then
191 |       # property is already set in the user provided config. In this case we don't override what has been set explicitly by the user.
192 |       echo "Skipping ${_property} for plugin ${_plugin_name} because it is already set"
193 |     else
194 |       if grep -o "^[^#]*" "${_neo4j_conf}" | grep -q --fixed-strings "${_property}=" ; then
195 |         sed --in-place "s/${_property}=/&${_value},/" "${_neo4j_conf}"
196 |       else
197 |         echo "${_property}=${_value}" >> "${_neo4j_conf}"
198 |       fi
199 |     fi
200 |   done
201 | }
202 | 
203 | function install_neo4j_labs_plugins
204 | {
205 |   # We store a copy of the config before we modify it for the plugins to allow us to see if there are user-set values in the input config that we shouldn't override
206 |   local _old_config="$(mktemp)"
207 |   cp "${NEO4J_HOME}"/conf/neo4j.conf "${_old_config}"
208 |   for plugin_name in $(echo "${NEO4JLABS_PLUGINS}" | jq --raw-output '.[]'); do
209 |     load_plugin_from_github "${plugin_name}"
210 |     apply_plugin_default_configuration "${plugin_name}" "${_old_config}"
211 |   done
212 |   rm "${_old_config}"
213 | }
214 | 
215 | function add_docker_default_to_conf
216 | {
217 |     # docker defaults should NOT overwrite values already in the conf file
218 |     local _setting="${1}"
219 |     local _value="${2}"
220 |     local _neo4j_home="${3}"
221 | 
222 |     if ! grep -q "^${_setting}=" "${_neo4j_home}"/conf/neo4j.conf
223 |     then
224 |         echo -e "\n"${_setting}=${_value} >> "${_neo4j_home}"/conf/neo4j.conf
225 |     fi
226 | }
227 | 
228 | function add_env_setting_to_conf
229 | {
230 |     # settings from environment variables should overwrite values already in the conf
231 |     local _setting=${1}
232 |     local _value=${2}
233 |     local _neo4j_home=${3}
234 | 
235 |     if grep -q -F "${_setting}=" "${_neo4j_home}"/conf/neo4j.conf; then
236 |         # Remove any lines containing the setting already
237 |         sed --in-place "/^${_setting}=.*/d" "${_neo4j_home}"/conf/neo4j.conf
238 |     fi
239 |     # Then always append setting to file
240 |     echo "${_setting}=${_value}" >> "${_neo4j_home}"/conf/neo4j.conf
241 | }
242 | 
243 | function set_initial_password
244 | {
245 |     local _neo4j_auth="${1}"
246 | 
247 |     # set the neo4j initial password only if you run the database server
248 |     if [ "${cmd}" == "neo4j" ]; then
249 |         if [ "${_neo4j_auth:-}" == "none" ]; then
250 |             add_env_setting_to_conf "dbms.security.auth_enabled" "false" "${NEO4J_HOME}"
251 |             # NEO4J_dbms_security_auth__enabled=false
252 |         elif [[ "${_neo4j_auth:-}" =~ ^([^/]+)\/([^/]+)/?([tT][rR][uU][eE])?$ ]]; then
253 |             admin_user="${BASH_REMATCH[1]}"
254 |             password="${BASH_REMATCH[2]}"
255 |             do_reset="${BASH_REMATCH[3]}"
256 | 
257 |             if [ "${password}" == "neo4j" ]; then
258 |                 echo >&2 "Invalid value for password. It cannot be 'neo4j', which is the default."
259 |                 exit 1
260 |             fi
261 |             if [ "${admin_user}" != "neo4j" ]; then
262 |                 echo >&2 "Invalid admin username, it must be neo4j"
263 |                 exit 1
264 |             fi
265 | 
266 |             if running_as_root; then
267 |                 # running set-initial-password as root will create subfolders to /data as root, causing startup fail when neo4j can't read or write the /data/dbms folder
268 |                 # creating the folder first will avoid that
269 |                 mkdir -p /data/dbms
270 |                 chown "${userid}":"${groupid}" /data/dbms
271 |             fi
272 | 
273 |             # Will exit with error if users already exist (and print a message explaining that)
274 |             # we probably don't want the message though, since it throws an error message on restarting the container.
275 |             if [ "${do_reset}" == "true" ]; then
276 |                 neo4j-admin set-initial-password "${password}" --require-password-change 2>/dev/null || true
277 |             else
278 |                 neo4j-admin set-initial-password "${password}" 2>/dev/null || true
279 |             fi
280 |         elif [ -n "${_neo4j_auth:-}" ]; then
281 |             echo "$_neo4j_auth is invalid"
282 |             echo >&2 "Invalid value for NEO4J_AUTH: '${_neo4j_auth}'"
283 |             exit 1
284 |         fi
285 |     fi
286 | }
287 | 
288 | # If we're running as root, then run as the neo4j user. Otherwise
289 | # docker is running with --user and we simply use that user.  Note
290 | # that su-exec, despite its name, does not replicate the functionality
291 | # of exec, so we need to use both
292 | if running_as_root; then
293 |   userid="neo4j"
294 |   groupid="neo4j"
295 |   groups=($(id -G neo4j))
296 |   exec_cmd="exec gosu neo4j:neo4j"
297 | else
298 |   userid="$(id -u)"
299 |   groupid="$(id -g)"
300 |   groups=($(id -G))
301 |   exec_cmd="exec"
302 | fi
303 | readonly userid
304 | readonly groupid
305 | readonly groups
306 | readonly exec_cmd
307 | 
308 | 
309 | # Need to chown the home directory - but a user might have mounted a
310 | # volume here (notably a conf volume). So take care not to chown
311 | # volumes (stuff not owned by neo4j)
312 | if running_as_root; then
313 |     # Non-recursive chown for the base directory
314 |     chown "${userid}":"${groupid}" "${NEO4J_HOME}"
315 |     chmod 700 "${NEO4J_HOME}"
316 |     find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -type d -exec chown -R ${userid}:${groupid} {} \;
317 |     find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -type d -exec chmod -R 700 {} \;
318 | fi
319 | 
320 | # Only prompt for license agreement if command contains "neo4j" in it
321 | # ==== CHECK LICENSE AGREEMENT ====
322 | 
323 | if [[ "${cmd}" == *"neo4j"* ]]; then
324 |   if [ "${NEO4J_EDITION}" == "enterprise" ]; then
325 |     if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then
326 |       echo >&2 "
327 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
328 | 
329 | (c) Neo4j Sweden AB.  2022.  All Rights Reserved.
330 | Use of this Software without a proper commercial license with Neo4j,
331 | Inc. or its affiliates is prohibited.
332 | 
333 | Email inquiries can be directed to: [email protected]
334 | 
335 | More information is also available at: https://neo4j.com/licensing/
336 | 
337 | 
338 | To accept the license agreement set the environment variable
339 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
340 | 
341 | To do this you can use the following docker argument:
342 | 
343 |         --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
344 | "
345 |       exit 1
346 |     fi
347 |   fi
348 | fi
349 | 
350 | # Env variable naming convention:
351 | # ==== RENAME LEGACY ENVIRONMENT CONF VARIABLES ====
352 | 
353 | # - prefix NEO4J_
354 | # - double underscore char '__' instead of single underscore '_' char in the setting name
355 | # - underscore char '_' instead of dot '.' char in the setting name
356 | # Example:
357 | # NEO4J_dbms_tx__log_rotation_retention__policy env variable to set
358 | #       dbms.tx_log.rotation.retention_policy setting
359 | 
360 | # Backward compatibility - map old hardcoded env variables into new naming convention (if they aren't set already)
361 | # Set some to default values if unset
362 | : ${NEO4J_dbms_tx__log_rotation_retention__policy:=${NEO4J_dbms_txLog_rotation_retentionPolicy:-}}
363 | : ${NEO4J_wrapper_java_additional:=${NEO4J_UDC_SOURCE:-"-Dneo4j.ext.udc.source=docker"}}
364 | : ${NEO4J_dbms_unmanaged__extension__classes:=${NEO4J_dbms_unmanagedExtensionClasses:-}}
365 | : ${NEO4J_dbms_allow__format__migration:=${NEO4J_dbms_allowFormatMigration:-}}
366 | : ${NEO4J_dbms_connectors_default__advertised__address:=${NEO4J_dbms_connectors_defaultAdvertisedAddress:-}}
367 | 
368 | if [ "${NEO4J_EDITION}" == "enterprise" ];
369 |   then
370 |    : ${NEO4J_causal__clustering_expected__core__cluster__size:=${NEO4J_causalClustering_expectedCoreClusterSize:-}}
371 |    : ${NEO4J_causal__clustering_initial__discovery__members:=${NEO4J_causalClustering_initialDiscoveryMembers:-}}
372 |    : ${NEO4J_causal__clustering_discovery__advertised__address:=${NEO4J_causalClustering_discoveryAdvertisedAddress:-}}
373 |    : ${NEO4J_causal__clustering_transaction__advertised__address:=${NEO4J_causalClustering_transactionAdvertisedAddress:-}}
374 |    : ${NEO4J_causal__clustering_raft__advertised__address:=${NEO4J_causalClustering_raftAdvertisedAddress:-}}
375 | fi
376 | 
377 | # unset old hardcoded unsupported env variables
378 | unset NEO4J_dbms_txLog_rotation_retentionPolicy NEO4J_UDC_SOURCE \
379 |     NEO4J_dbms_unmanagedExtensionClasses NEO4J_dbms_allowFormatMigration \
380 |     NEO4J_dbms_connectors_defaultAdvertisedAddress NEO4J_ha_serverId \
381 |     NEO4J_ha_initialHosts NEO4J_causalClustering_expectedCoreClusterSize \
382 |     NEO4J_causalClustering_initialDiscoveryMembers \
383 |     NEO4J_causalClustering_discoveryListenAddress \
384 |     NEO4J_causalClustering_discoveryAdvertisedAddress \
385 |     NEO4J_causalClustering_transactionListenAddress \
386 |     NEO4J_causalClustering_transactionAdvertisedAddress \
387 |     NEO4J_causalClustering_raftListenAddress \
388 |     NEO4J_causalClustering_raftAdvertisedAddress
389 | 
390 | 
391 | # ==== CHECK FILE PERMISSIONS ON MOUNTED FOLDERS ====
392 | 
393 | if [ -d /conf ]; then
394 |     if secure_mode_enabled; then
395 | 	    check_mounted_folder_readable "/conf"
396 |     fi
397 |     find /conf -type f -exec cp {} "${NEO4J_HOME}"/conf \;
398 | fi
399 | 
400 | if [ -d /ssl ]; then
401 |     if secure_mode_enabled; then
402 |     	check_mounted_folder_readable "/ssl"
403 |     fi
404 |     : ${NEO4J_dbms_directories_certificates:="/ssl"}
405 | fi
406 | 
407 | if [ -d /plugins ]; then
408 |     if secure_mode_enabled; then
409 |         if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then
410 |             # We need write permissions
411 |             check_mounted_folder_writable_with_chown "/plugins"
412 |         fi
413 |         check_mounted_folder_readable "/plugins"
414 |     fi
415 |     : ${NEO4J_dbms_directories_plugins:="/plugins"}
416 | fi
417 | 
418 | if [ -d /import ]; then
419 |     if secure_mode_enabled; then
420 |         check_mounted_folder_readable "/import"
421 |     fi
422 |     : ${NEO4J_dbms_directories_import:="/import"}
423 | fi
424 | 
425 | if [ -d /metrics ]; then
426 |     if [ "${NEO4J_EDITION}" == "enterprise" ]; then
427 |         if secure_mode_enabled; then
428 |             check_mounted_folder_readable "/metrics"
429 |         fi
430 |         : ${NEO4J_dbms_directories_metrics:="/metrics"}
431 |     fi
432 | fi
433 | 
434 | if [ -d /logs ]; then
435 |     check_mounted_folder_writable_with_chown "/logs"
436 |     : ${NEO4J_dbms_directories_logs:="/logs"}
437 | fi
438 | 
439 | if [ -d /data ]; then
440 |     check_mounted_folder_writable_with_chown "/data"
441 |     if [ -d /data/databases ]; then
442 |         check_mounted_folder_writable_with_chown "/data/databases"
443 |     fi
444 |     if [ -d /data/dbms ]; then
445 |         check_mounted_folder_writable_with_chown "/data/dbms"
446 |     fi
447 | fi
448 | 
449 | # ==== SET CONFIGURATIONS ====
450 | 
451 | ## == DOCKER SPECIFIC DEFAULT CONFIGURATIONS ===
452 | ## these should not override *any* configurations set by the user
453 | 
454 | add_docker_default_to_conf "dbms.tx_log.rotation.retention_policy" "100M size" "${NEO4J_HOME}"
455 | add_docker_default_to_conf "dbms.memory.pagecache.size" "512M" "${NEO4J_HOME}"
456 | add_docker_default_to_conf "dbms.connectors.default_listen_address" "0.0.0.0" "${NEO4J_HOME}"
457 | add_docker_default_to_conf "dbms.connector.https.listen_address" "0.0.0.0:7473" "${NEO4J_HOME}"
458 | add_docker_default_to_conf "dbms.connector.http.listen_address" "0.0.0.0:7474" "${NEO4J_HOME}"
459 | add_docker_default_to_conf "dbms.connector.bolt.listen_address" "0.0.0.0:7687" "${NEO4J_HOME}"
460 | # set enterprise only docker defaults
461 | if [ "${NEO4J_EDITION}" == "enterprise" ];
462 | then
463 |     add_docker_default_to_conf "causal_clustering.discovery_advertised_address" "$(hostname):5000" "${NEO4J_HOME}"
464 |     add_docker_default_to_conf "causal_clustering.transaction_advertised_address" "$(hostname):6000" "${NEO4J_HOME}"
465 |     add_docker_default_to_conf "causal_clustering.raft_advertised_address" "$(hostname):7000" "${NEO4J_HOME}"
466 | fi
467 | 
468 | ## == ENVIRONMENT VARIABLE CONFIGURATIONS ===
469 | ## these override BOTH defaults and any existing values in the neo4j.conf file
470 | 
471 | #The udc.source=tarball should be replaced by udc.source=docker in both dbms.jvm.additional and wrapper.java.additional
472 | #Using sed to replace only this part will allow the custom configs to be added after, separated by a ,.
473 | if grep -q "udc.source=tarball" "${NEO4J_HOME}"/conf/neo4j.conf; then
474 |      sed -i -e 's/udc.source=tarball/udc.source=docker/g' "${NEO4J_HOME}"/conf/neo4j.conf
475 | fi
476 | #The udc.source should always be set to docker by default and we have to allow also custom configs to be added after that.
477 | #In this case, this piece of code helps to add the default value and a , to support custom configs after.
478 | if ! grep -q "dbms.jvm.additional=-Dunsupported.dbms.udc.source=docker" "${NEO4J_HOME}"/conf/neo4j.conf; then
479 |   sed -i -e 's/dbms.jvm.additional=/dbms.jvm.additional=-Dunsupported.dbms.udc.source=docker,/g' "${NEO4J_HOME}"/conf/neo4j.conf
480 | fi
481 | 
482 | # save NEO4J_HOME and NEO4J_AUTH to temp variables that don't begin with NEO4J_ so they don't get added to the conf
483 | temp_neo4j_home="${NEO4J_HOME}"
484 | temp_neo4j_auth="${NEO4J_AUTH:-}"
485 | # list env variables with prefix NEO4J_ and create settings from them
486 | unset NEO4J_AUTH NEO4J_SHA256 NEO4J_TARBALL NEO4J_EDITION NEO4J_ACCEPT_LICENSE_AGREEMENT NEO4J_HOME
487 | for i in $( set | grep ^NEO4J_ | awk -F'=' '{print $1}' | sort -rn ); do
488 |     setting=$(echo "${i}" | sed 's|^NEO4J_||' | sed 's|_|.|g' | sed 's|\.\.|_|g')
489 |     value=$(echo "${!i}")
490 |     # Don't allow settings with no value or settings that start with a number (neo4j converts settings to env variables and you cannot have an env variable that starts with a number)
491 |     if [[ -n ${value} ]]; then
492 |         if [[ ! "${setting}" =~ ^[0-9]+.*$ ]]; then
493 |             add_env_setting_to_conf "${setting}" "${value}" "${temp_neo4j_home}"
494 |         else
495 |             echo >&2 "WARNING: ${setting} not written to conf file because settings that start with a number are not permitted"
496 |         fi
497 |     fi
498 | done
499 | export NEO4J_HOME="${temp_neo4j_home}"
500 | unset temp_neo4j_home
501 | 
502 | # ==== SET PASSWORD AND PLUGINS ====
503 | 
504 | set_initial_password "${temp_neo4j_auth}"
505 | 
506 | 
507 | if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then
508 |   # NEO4JLABS_PLUGINS should be a json array of plugins like '["graph-algorithms", "apoc", "streams", "graphql"]'
509 |   install_neo4j_labs_plugins
510 | fi
511 | 
512 | # ==== INVOKE NEO4J STARTUP ====
513 | 
514 | [ -f "${EXTENSION_SCRIPT:-}" ] && . ${EXTENSION_SCRIPT}
515 | 
516 | if [ "${cmd}" == "dump-config" ]; then
517 |     if [ ! -d "/conf" ]; then
518 |         echo >&2 "You must mount a folder to /conf so that the configuration file(s) can be dumped to there."
519 |         exit 1
520 |     fi
521 |     check_mounted_folder_writable_with_chown "/conf"
522 |     cp --recursive "${NEO4J_HOME}"/conf/* /conf
523 |     echo "Config Dumped"
524 |     exit 0
525 | fi
526 | 
527 | # Use su-exec to drop privileges to neo4j user
528 | # Note that su-exec, despite its name, does not replicate the
529 | # functionality of exec, so we need to use both
530 | if [ "${cmd}" == "neo4j" ]; then
531 |   ${exec_cmd} neo4j console
532 | else
533 |   ${exec_cmd} "$@"
534 | fi
535 | 
```
Page 4/7FirstPrevNextLast