<!DOCTYPE html>
<html lang='en'>
<head>
<title>nextcloud-server.git - Nextcloud server, a safe home for all your data: https://github.com/nextcloud/server</title>
<meta name='generator' content='cgit v1.2.3'/>
<meta name='robots' content='index, nofollow'/>
<link rel='stylesheet' type='text/css' href='/cgit.css'/>
<link rel='shortcut icon' href='/favicon.ico'/>
<link rel='alternate' title='Atom feed' href='https://source.dussan.org/nextcloud-server.git/atom/tests/lib/Group/HideFromCollaborationTest.php?h=master' type='application/atom+xml'/>
</head>
<body>
<div id='cgit'><table id='header'>
<tr>
<td class='logo' rowspan='2'><a href='/'><img src='/cgit.png' alt='cgit logo'/></a></td>
<td class='main'><a href='/'>index</a> : <a href='/nextcloud-server.git/'>nextcloud-server.git</a></td><td class='form'><form method='get'>
<input type='hidden' name='id2' value='1d451d44a2bb358f711ae1f91b0bea121e2fa9cd'/><select name='h' onchange='this.form.submit();'>
<option value='27-shared-null-storage-merged'>27-shared-null-storage-merged</option>
<option value='3rdparty-aws-diet'>3rdparty-aws-diet</option>
<option value='3rdparty/justinrainbow/json-schema'>3rdparty/justinrainbow/json-schema</option>
<option value='3rdparty/stable28/sabre-dav-itip-broker'>3rdparty/stable28/sabre-dav-itip-broker</option>
<option value='3rdparty/stable29/sabre-dav-itip-broker'>3rdparty/stable29/sabre-dav-itip-broker</option>
<option value='3rdparty/stable30/sabre-dav-itip-broker'>3rdparty/stable30/sabre-dav-itip-broker</option>
<option value='44287-fix-avatar-fed-share-receiver'>44287-fix-avatar-fed-share-receiver</option>
<option value='44319-fix-fed-share-user-avatars'>44319-fix-fed-share-user-avatars</option>
<option value='Dennis1993-patch-1'>Dennis1993-patch-1</option>
<option value='Fix/app-menu-overflow'>Fix/app-menu-overflow</option>
<option value='Fix/mp3_regex'>Fix/mp3_regex</option>
<option value='Jerome-Herbinet-better-devices-wipe-action-wording'>Jerome-Herbinet-better-devices-wipe-action-wording</option>
<option value='Jerome-Herbinet-better-new-wording-better-than-delete-and-unshare'>Jerome-Herbinet-better-new-wording-better-than-delete-and-unshare</option>
<option value='Jerome-Herbinet-folder-tree-structure'>Jerome-Herbinet-folder-tree-structure</option>
<option value='Jerome-Herbinet-internal-shares-parameter-better-distinction'>Jerome-Herbinet-internal-shares-parameter-better-distinction</option>
<option value='Jerome-Herbinet-patch-1'>Jerome-Herbinet-patch-1</option>
<option value='Jerome-Herbinet-replace-remove-wording-with-delete'>Jerome-Herbinet-replace-remove-wording-with-delete</option>
<option value='Valdnet-patch-1'>Valdnet-patch-1</option>
<option value='add-VersionCreatedEvent'>add-VersionCreatedEvent</option>
<option value='add-caldav-repair-middleware'>add-caldav-repair-middleware</option>
<option value='add-command-to-cleanup-preview'>add-command-to-cleanup-preview</option>
<option value='add-default-request-timeout-const'>add-default-request-timeout-const</option>
<option value='add-integration-tests-for-deleting-a-file-copied-from-a-share'>add-integration-tests-for-deleting-a-file-copied-from-a-share</option>
<option value='add-integration-tests-for-moving-a-file-from-and-to-a-shared-folder'>add-integration-tests-for-moving-a-file-from-and-to-a-shared-folder</option>
<option value='add-integration-tests-for-renaming-a-share-by-a-user-with-stale-shares'>add-integration-tests-for-renaming-a-share-by-a-user-with-stale-shares</option>
<option value='addAllToGroupBy'>addAllToGroupBy</option>
<option value='addConstrDep'>addConstrDep</option>
<option value='admin-audit-listen-failed-login'>admin-audit-listen-failed-login</option>
<option value='admin_audit/enh/move-to-event-listeners'>admin_audit/enh/move-to-event-listeners</option>
<option value='allowMkcolAndPut'>allowMkcolAndPut</option>
<option value='allowUnderCustLink'>allowUnderCustLink</option>
<option value='appStoreCacheFolder'>appStoreCacheFolder</option>
<option value='appStoreDisabledOcc'>appStoreDisabledOcc</option>
<option value='artonge/backport/52121/stable29'>artonge/backport/52121/stable29</option>
<option value='artonge/backport/stable27/47770'>artonge/backport/stable27/47770</option>
<option value='artonge/backport/stable29/48207'>artonge/backport/stable29/48207</option>
<option value='artonge/chore/increase_log_in_trashbin'>artonge/chore/increase_log_in_trashbin</option>
<option value='artonge/chore/ldap_wizard_rewrite'>artonge/chore/ldap_wizard_rewrite</option>
<option value='artonge/chore/update_nc_cypress_beta.11'>artonge/chore/update_nc_cypress_beta.11</option>
<option value='artonge/chore/update_nc_pwd_confirmation_v5.3.1'>artonge/chore/update_nc_pwd_confirmation_v5.3.1</option>
<option value='artonge/chore/update_password_confirmation_v5.3.1'>artonge/chore/update_password_confirmation_v5.3.1</option>
<option value='artonge/chore/user_retry_trait'>artonge/chore/user_retry_trait</option>
<option value='artonge/debt/core_main.js'>artonge/debt/core_main.js</option>
<option value='artonge/feat/add_retry_support_to_query_builder'>artonge/feat/add_retry_support_to_query_builder</option>
<option value='artonge/feat/allow_deleting_metadata'>artonge/feat/allow_deleting_metadata</option>
<option value='artonge/feat/allow_partial_seen_users'>artonge/feat/allow_partial_seen_users</option>
<option value='artonge/feat/compare_hashed_password_when_updating_global_cred_in_files_external'>artonge/feat/compare_hashed_password_when_updating_global_cred_in_files_external</option>
<option value='artonge/feat/do_not_require_samesite_strict_cookie_on_public.php'>artonge/feat/do_not_require_samesite_strict_cookie_on_public.php</option>
<option value='artonge/feat/files_trashbin_parallel_expire_job'>artonge/feat/files_trashbin_parallel_expire_job</option>
<option value='artonge/feat/forward_sizeDifference_on_write'>artonge/feat/forward_sizeDifference_on_write</option>
<option value='artonge/feat/implement_custom_updater_for_object_storage'>artonge/feat/implement_custom_updater_for_object_storage</option>
<option value='artonge/feat/implement_custom_updater_for_object_storage-squashed'>artonge/feat/implement_custom_updater_for_object_storage-squashed</option>
<option value='artonge/feat/implement_custom_updater_for_object_storage-squashed-29'>artonge/feat/implement_custom_updater_for_object_storage-squashed-29</option>
<option value='artonge/feat/maintenance_warn_encrypt_all'>artonge/feat/maintenance_warn_encrypt_all</option>
<option value='artonge/feat/pwd_confirmation_allow_set_period'>artonge/feat/pwd_confirmation_allow_set_period</option>
<option value='artonge/feat/reset_route_when_sidebar_and_viewer_are_close'>artonge/feat/reset_route_when_sidebar_and_viewer_are_close</option>
<option value='artonge/feat/skip-trash-header'>artonge/feat/skip-trash-header</option>
<option value='artonge/feat/validate_keychecksum'>artonge/feat/validate_keychecksum</option>
<option value='artonge/fix/access_list_unaccepted_shares'>artonge/fix/access_list_unaccepted_shares</option>
<option value='artonge/fix/attribute_in_sidebar'>artonge/fix/attribute_in_sidebar</option>
<option value='artonge/fix/catch_exception_in_expire_trash'>artonge/fix/catch_exception_in_expire_trash</option>
<option value='artonge/fix/color_debounce'>artonge/fix/color_debounce</option>
<option value='artonge/fix/copy_cache_during_copy_operations'>artonge/fix/copy_cache_during_copy_operations</option>
<option value='artonge/fix/copy_subfolders_s3'>artonge/fix/copy_subfolders_s3</option>
<option value='artonge/fix/count_users'>artonge/fix/count_users</option>
<option value='artonge/fix/displayname_in_sidebar'>artonge/fix/displayname_in_sidebar</option>
<option value='artonge/fix/do_not_pass_parent_for_activity_tab_comments_plugin'>artonge/fix/do_not_pass_parent_for_activity_tab_comments_plugin</option>
<option value='artonge/fix/drop_unecessary_exit'>artonge/fix/drop_unecessary_exit</option>
<option value='artonge/fix/epehmeral_sessions'>artonge/fix/epehmeral_sessions</option>
<option value='artonge/fix/extra_files_integrity_check'>artonge/fix/extra_files_integrity_check</option>
<option value='artonge/fix/file_list_jump_on_viewer_close'>artonge/fix/file_list_jump_on_viewer_close</option>
<option value='artonge/fix/forward_extra'>artonge/fix/forward_extra</option>
<option value='artonge/fix/generate_metadata_perf'>artonge/fix/generate_metadata_perf</option>
<option value='artonge/fix/getting_cache_entry'>artonge/fix/getting_cache_entry</option>
<option value='artonge/fix/handle_folders_copy_live_photos'>artonge/fix/handle_folders_copy_live_photos</option>
<option value='artonge/fix/hash_return_type'>artonge/fix/hash_return_type</option>
<option value='artonge/fix/listen_to_group_removal_for_share'>artonge/fix/listen_to_group_removal_for_share</option>
<option value='artonge/fix/login_flow_v2_sessions'>artonge/fix/login_flow_v2_sessions</option>
<option value='artonge/fix/login_flow_v2_sessions_2'>artonge/fix/login_flow_v2_sessions_2</option>
<option value='artonge/fix/make_error_a_info'>artonge/fix/make_error_a_info</option>
<option value='artonge/fix/metadata_field_search'>artonge/fix/metadata_field_search</option>
<option value='artonge/fix/min_age_rentention_files_versions'>artonge/fix/min_age_rentention_files_versions</option>
<option value='artonge/fix/missing_dot'>artonge/fix/missing_dot</option>
<option value='artonge/fix/opening_a_secondly_created_file'>artonge/fix/opening_a_secondly_created_file</option>
<option value='artonge/fix/prevent_missing_users_from_crashing_disabled_users_search'>artonge/fix/prevent_missing_users_from_crashing_disabled_users_search</option>
<option value='artonge/fix/publicpage_attribute'>artonge/fix/publicpage_attribute</option>
<option value='artonge/fix/remove_duplicate_propfind_call'>artonge/fix/remove_duplicate_propfind_call</option>
<option value='artonge/fix/remove_unnecessary_formating'>artonge/fix/remove_unnecessary_formating</option>
<option value='artonge/fix/restoring_encrypted_version'>artonge/fix/restoring_encrypted_version</option>
<option value='artonge/fix/sidebar_davpath'>artonge/fix/sidebar_davpath</option>
<option value='artonge/fix/skip_blurhash_if_previews_are_disabled'>artonge/fix/skip_blurhash_if_previews_are_disabled</option>
<option value='artonge/fix/storage_full_warning'>artonge/fix/storage_full_warning</option>
<option value='artonge/fix/systemtag_rendering'>artonge/fix/systemtag_rendering</option>
<option value='artonge/fix/transfer_ownership'>artonge/fix/transfer_ownership</option>
<option value='artonge/fix/uid_type'>artonge/fix/uid_type</option>
<option value='artonge/fix/use_loginname_to_check_password'>artonge/fix/use_loginname_to_check_password</option>
<option value='artonge/fix/use_preview_api_for_blurhash_generation'>artonge/fix/use_preview_api_for_blurhash_generation</option>
<option value='artonge/fix/vue_app_names'>artonge/fix/vue_app_names</option>
<option value='artonge/local/IONOS/feat/config_unified_search_providers_allowed'>artonge/local/IONOS/feat/config_unified_search_providers_allowed</option>
<option value='artonge/local/IONOS/tl/dev/disable-mailing'>artonge/local/IONOS/tl/dev/disable-mailing</option>
<option value='artonge/local/georglauterbach/feat/skip_cname'>artonge/local/georglauterbach/feat/skip_cname</option>
<option value='artonge/optim/close_connection_before_s3_upload'>artonge/optim/close_connection_before_s3_upload</option>
<option value='artonge/support_oc_checksum_in_bulk_upload'>artonge/support_oc_checksum_in_bulk_upload</option>
<option value='artonge/test/remove_only_calls'>artonge/test/remove_only_calls</option>
<option value='authPropertyPromotion'>authPropertyPromotion</option>
<option value='automated/noid/-update-min-supported-desktop-version'>automated/noid/-update-min-supported-desktop-version</option>
<option value='automated/noid/main-fix-npm-audit'>automated/noid/main-fix-npm-audit</option>
<option value='automated/noid/master-fix-npm-audit'>automated/noid/master-fix-npm-audit</option>
<option value='automated/noid/master-update-ca-cert-bundle'>automated/noid/master-update-ca-cert-bundle</option>
<option value='automated/noid/master-update-code-signing-crl'>automated/noid/master-update-code-signing-crl</option>
<option value='automated/noid/master-update-psalm-baseline'>automated/noid/master-update-psalm-baseline</option>
<option value='automated/noid/stable22-update-ca-cert-bundle'>automated/noid/stable22-update-ca-cert-bundle</option>
<option value='automated/noid/stable22-update-code-signing-crl'>automated/noid/stable22-update-code-signing-crl</option>
<option value='automated/noid/stable23-update-ca-cert-bundle'>automated/noid/stable23-update-ca-cert-bundle</option>
<option value='automated/noid/stable23-update-code-signing-crl'>automated/noid/stable23-update-code-signing-crl</option>
<option value='automated/noid/stable24-update-ca-cert-bundle'>automated/noid/stable24-update-ca-cert-bundle</option>
<option value='automated/noid/stable24-update-code-signing-crl'>automated/noid/stable24-update-code-signing-crl</option>
<option value='automated/noid/stable25-update-ca-cert-bundle'>automated/noid/stable25-update-ca-cert-bundle</option>
<option value='automated/noid/stable25-update-code-signing-crl'>automated/noid/stable25-update-code-signing-crl</option>
<option value='automated/noid/stable26-update-ca-cert-bundle'>automated/noid/stable26-update-ca-cert-bundle</option>
<option value='automated/noid/stable26-update-code-signing-crl'>automated/noid/stable26-update-code-signing-crl</option>
<option value='automated/noid/stable27-fix-npm-audit'>automated/noid/stable27-fix-npm-audit</option>
<option value='automated/noid/stable27-update-ca-cert-bundle'>automated/noid/stable27-update-ca-cert-bundle</option>
<option value='automated/noid/stable27-update-code-signing-crl'>automated/noid/stable27-update-code-signing-crl</option>
<option value='automated/noid/stable28-fix-npm-audit'>automated/noid/stable28-fix-npm-audit</option>
<option value='automated/noid/stable28-update-ca-cert-bundle'>automated/noid/stable28-update-ca-cert-bundle</option>
<option value='automated/noid/stable28-update-code-signing-crl'>automated/noid/stable28-update-code-signing-crl</option>
<option value='automated/noid/stable28-update-psalm-baseline'>automated/noid/stable28-update-psalm-baseline</option>
<option value='automated/noid/stable29-fix-npm-audit'>automated/noid/stable29-fix-npm-audit</option>
<option value='automated/noid/stable29-update-ca-cert-bundle'>automated/noid/stable29-update-ca-cert-bundle</option>
<option value='automated/noid/stable29-update-code-signing-crl'>automated/noid/stable29-update-code-signing-crl</option>
<option value='automated/noid/stable29-update-psalm-baseline'>automated/noid/stable29-update-psalm-baseline</option>
<option value='automated/noid/stable30-fix-npm-audit'>automated/noid/stable30-fix-npm-audit</option>
<option value='automated/noid/stable30-update-ca-cert-bundle'>automated/noid/stable30-update-ca-cert-bundle</option>
<option value='automated/noid/stable30-update-code-signing-crl'>automated/noid/stable30-update-code-signing-crl</option>
<option value='automated/noid/stable30-update-psalm-baseline'>automated/noid/stable30-update-psalm-baseline</option>
<option value='automated/noid/stable31-fix-npm-audit'>automated/noid/stable31-fix-npm-audit</option>
<option value='automated/noid/stable31-update-ca-cert-bundle'>automated/noid/stable31-update-ca-cert-bundle</option>
<option value='automated/noid/stable31-update-code-signing-crl'>automated/noid/stable31-update-code-signing-crl</option>
<option value='automated/update-workflows/default'>automated/update-workflows/default</option>
<option value='avatar-work-29'>avatar-work-29</option>
<option value='avoidStatusSessions'>avoidStatusSessions</option>
<option value='background-job-list-reserved'>background-job-list-reserved</option>
<option value='background-scan-eq'>background-scan-eq</option>
<option value='backport/29-openfile'>backport/29-openfile</option>
<option value='backport/30/fix_move_on_same_bucket'>backport/30/fix_move_on_same_bucket</option>
<option value='backport/38630/stable28'>backport/38630/stable28</option>
<option value='backport/38630/stable29'>backport/38630/stable29</option>
<option value='backport/38630/stable30'>backport/38630/stable30</option>
<option value='backport/39607/stable26'>backport/39607/stable26</option>
<option value='backport/39611/stable30'>backport/39611/stable30</option>
<option value='backport/40367/stable25'>backport/40367/stable25</option>
<option value='backport/40367/stable26'>backport/40367/stable26</option>
<option value='backport/40394/stable25'>backport/40394/stable25</option>
<option value='backport/40394/stable26'>backport/40394/stable26</option>
<option value='backport/40394/stable27'>backport/40394/stable27</option>
<option value='backport/40394/stable28'>backport/40394/stable28</option>
<option value='backport/40394/stable29'>backport/40394/stable29</option>
<option value='backport/40394/stable30'>backport/40394/stable30</option>
<option value='backport/41065/stable29'>backport/41065/stable29</option>
<option value='backport/41065/stable30'>backport/41065/stable30</option>
<option value='backport/41065/stable31'>backport/41065/stable31</option>
<option value='backport/41327/stable26'>backport/41327/stable26</option>
<option value='backport/42099/stable29'>backport/42099/stable29</option>
<option value='backport/42099/stable31'>backport/42099/stable31</option>
<option value='backport/42170/stable28'>backport/42170/stable28</option>
<option value='backport/42172/stable26'>backport/42172/stable26</option>
<option value='backport/42548/stable22'>backport/42548/stable22</option>
<option value='backport/42930/stable26'>backport/42930/stable26</option>
<option value='backport/42971/stable25'>backport/42971/stable25</option>
<option value='backport/43025/stable28'>backport/43025/stable28</option>
<option value='color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span><span class="cm">/*</span>
<span class="cm"> * Copyright 2013 Florian Zschocke</span>
<span class="cm"> * Copyright 2013 gitblit.com</span>
<span class="cm"> *</span>
<span class="cm"> * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</span>
<span class="cm"> * you may not use this file except in compliance with the License.</span>
<span class="cm"> * You may obtain a copy of the License at</span>
<span class="cm"> *</span>
<span class="cm"> *     http://www.apache.org/licenses/LICENSE-2.0</span>
<span class="cm"> *</span>
<span class="cm"> * Unless required by applicable law or agreed to in writing, software</span>
<span class="cm"> * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,</span>
<span class="cm"> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span>
<span class="cm"> * See the License for the specific language governing permissions and</span>
<span class="cm"> * limitations under the License.</span>
<span class="cm"> */</span>
<span class="kn">package</span><span class="w"> </span><span class="nn">com.gitblit.auth</span><span class="p">;</span>

<span class="kn">import</span><span class="w"> </span><span class="nn">java.io.File</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">java.io.FileInputStream</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">java.text.MessageFormat</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">java.util.Map</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">java.util.Scanner</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">java.util.concurrent.ConcurrentHashMap</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">java.util.regex.Matcher</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">java.util.regex.Pattern</span><span class="p">;</span>

<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.commons.codec.binary.Base64</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.commons.codec.digest.Crypt</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.commons.codec.digest.DigestUtils</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.commons.codec.digest.Md5Crypt</span><span class="p">;</span>

<span class="kn">import</span><span class="w"> </span><span class="nn">com.gitblit.Constants</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">com.gitblit.Constants.AccountType</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">com.gitblit.Constants.Role</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">com.gitblit.Keys</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">com.gitblit.models.TeamModel</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">com.gitblit.models.UserModel</span><span class="p">;</span>


<span class="cm">/**</span>
<span class="cm"> * Implementation of a user service using an Apache htpasswd file for authentication.</span>
<span class="cm"> *</span>
<span class="cm"> * This user service implement custom authentication using entries in a file created</span>
<span class="cm"> * by the &#39;htpasswd&#39; program of an Apache web server. All possible output</span>
<span class="cm"> * options of the &#39;htpasswd&#39; program version 2.2 are supported:</span>
<span class="cm"> * plain text (only on Windows and Netware),</span>
<span class="cm"> * glibc crypt() (not on Windows and NetWare),</span>
<span class="cm"> * Apache MD5 (apr1),</span>
<span class="cm"> * unsalted SHA-1.</span>
<span class="cm"> *</span>
<span class="cm"> * Configuration options:</span>
<span class="cm"> * realm.htpasswd.backingUserService - Specify the backing user service that is used</span>
<span class="cm"> *                                     to keep the user data other than the password.</span>
<span class="cm"> *                                     The default is &#39;${baseFolder}/users.conf&#39;.</span>
<span class="cm"> * realm.htpasswd.userfile - The text file with the htpasswd entries to be used for</span>
<span class="cm"> *                           authentication.</span>
<span class="cm"> *                           The default is &#39;${baseFolder}/htpasswd&#39;.</span>
<span class="cm"> * realm.htpasswd.overrideLocalAuthentication - Specify if local accounts are overwritten</span>
<span class="cm"> *                                              when authentication matches for an</span>
<span class="cm"> *                                              external account.</span>
<span class="cm"> *</span>
<span class="cm"> * @author Florian Zschocke</span>
<span class="cm"> *</span>
<span class="cm"> */</span>
<span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">HtpasswdAuthProvider</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">UsernamePasswordAuthenticationProvider</span><span class="w"> </span><span class="p">{</span>

<span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">KEY_HTPASSWD_FILE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Keys</span><span class="p">.</span><span class="na">realm</span><span class="p">.</span><span class="na">htpasswd</span><span class="p">.</span><span class="na">userfile</span><span class="p">;</span>
<span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">DEFAULT_HTPASSWD_FILE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;${baseFolder}/htpasswd&quot;</span><span class="p">;</span>

<span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">KEY_SUPPORT_PLAINTEXT_PWD</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;realm.htpasswd.supportPlaintextPasswords&quot;</span><span class="p">;</span>

<span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">supportPlainTextPwd</span><span class="p">;</span>

<span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">File</span><span class="w"> </span><span class="n">htpasswdFile</span><span class="p">;</span>

<span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">htUsers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ConcurrentHashMap</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="p">();</span>

<span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">volatile</span><span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">lastModified</span><span class="p">;</span>

<span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="nf">HtpasswdAuthProvider</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="kd">super</span><span class="p">(</span><span class="s">&quot;htpasswd&quot;</span><span class="p">);</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="cm">/**</span>
<span class="cm">     * Setup the user service.</span>
<span class="cm">     *</span>
<span class="cm">     * The HtpasswdUserService extends the GitblitUserService and is thus</span>
<span class="cm">     * backed by the available user services provided by the GitblitUserService.</span>
<span class="cm">     * In addition the setup tries to read and parse the htpasswd file to be used</span>
<span class="cm">     * for authentication.</span>
<span class="cm">     *</span>
<span class="cm">     * @param settings</span>
<span class="cm">     * @since 0.7.0</span>
<span class="cm">     */</span>
<span class="w">    </span><span class="nd">@Override</span>
<span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setup</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="n">String</span><span class="w"> </span><span class="n">os</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">getProperty</span><span class="p">(</span><span class="s">&quot;os.name&quot;</span><span class="p">).</span><span class="na">toLowerCase</span><span class="p">();</span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="na">startsWith</span><span class="p">(</span><span class="s">&quot;windows&quot;</span><span class="p">)</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">os</span><span class="p">.</span><span class="na">startsWith</span><span class="p">(</span><span class="s">&quot;netware&quot;</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w">            </span><span class="n">supportPlainTextPwd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w">            </span><span class="n">supportPlainTextPwd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span>
<span class="w">        </span><span class="p">}</span>
<span class="w">        </span><span class="n">read</span><span class="p">();</span>
<span class="w">        </span><span class="n">logger</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">&quot;Read &quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">htUsers</span><span class="p">.</span><span class="na">size</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot; users from htpasswd file: &quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">htpasswdFile</span><span class="p">);</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="nd">@Override</span>
<span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">supportsCredentialChanges</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="nd">@Override</span>
<span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">supportsDisplayNameChanges</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="nd">@Override</span>
<span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">supportsEmailAddressChanges</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="nd">@Override</span>
<span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">supportsTeamMembershipChanges</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="nd">@Override</span>
<span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">supportsRoleChanges</span><span class="p">(</span><span class="n">UserModel</span><span class="w"> </span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">Role</span><span class="w"> </span><span class="n">role</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">	</span><span class="nd">@Override</span>
<span class="w">	</span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">supportsRoleChanges</span><span class="p">(</span><span class="n">TeamModel</span><span class="w"> </span><span class="n">team</span><span class="p">,</span><span class="w"> </span><span class="n">Role</span><span class="w"> </span><span class="n">role</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">	</span><span class="p">}</span>

<span class="w">    </span><span class="cm">/**</span>
<span class="cm">     * Authenticate a user based on a username and password.</span>
<span class="cm">     *</span>
<span class="cm">     * If the account is determined to be a local account, authentication</span>
<span class="cm">     * will be done against the locally stored password.</span>
<span class="cm">     * Otherwise, the configured htpasswd file is read. All current output options</span>
<span class="cm">     * of htpasswd are supported: clear text, crypt(), Apache MD5 and unsalted SHA-1.</span>
<span class="cm">     *</span>
<span class="cm">     * @param username</span>
<span class="cm">     * @param password</span>
<span class="cm">     * @return a user object or null</span>
<span class="cm">     */</span>
<span class="w">    </span><span class="nd">@Override</span>
<span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">UserModel</span><span class="w"> </span><span class="nf">authenticate</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">username</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="o">[]</span><span class="w"> </span><span class="n">password</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="n">read</span><span class="p">();</span>
<span class="w">        </span><span class="n">String</span><span class="w"> </span><span class="n">storedPwd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">htUsers</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">username</span><span class="p">);</span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">storedPwd</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w">            </span><span class="kt">boolean</span><span class="w"> </span><span class="n">authenticated</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span>
<span class="w">            </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">passwd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">String</span><span class="p">(</span><span class="n">password</span><span class="p">);</span>

<span class="w">            </span><span class="c1">// test Apache MD5 variant encrypted password</span>
<span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">storedPwd</span><span class="p">.</span><span class="na">startsWith</span><span class="p">(</span><span class="s">&quot;$apr1$&quot;</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w">                </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">storedPwd</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">Md5Crypt</span><span class="p">.</span><span class="na">apr1Crypt</span><span class="p">(</span><span class="n">passwd</span><span class="p">,</span><span class="w"> </span><span class="n">storedPwd</span><span class="p">)))</span><span class="w"> </span><span class="p">{</span>
<span class="w">                    </span><span class="n">logger</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">&quot;Apache MD5 encoded password matched for user &#39;&quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">username</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot;&#39;&quot;</span><span class="p">);</span>
<span class="w">                    </span><span class="n">authenticated</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">                </span><span class="p">}</span>
<span class="w">            </span><span class="p">}</span>
<span class="w">            </span><span class="c1">// test unsalted SHA password</span>
<span class="w">            </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">storedPwd</span><span class="p">.</span><span class="na">startsWith</span><span class="p">(</span><span class="s">&quot;{SHA}&quot;</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w">                </span><span class="n">String</span><span class="w"> </span><span class="n">passwd64</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Base64</span><span class="p">.</span><span class="na">encodeBase64String</span><span class="p">(</span><span class="n">DigestUtils</span><span class="p">.</span><span class="na">sha1</span><span class="p">(</span><span class="n">passwd</span><span class="p">));</span>
<span class="w">                </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">storedPwd</span><span class="p">.</span><span class="na">substring</span><span class="p">(</span><span class="s">&quot;{SHA}&quot;</span><span class="p">.</span><span class="na">length</span><span class="p">()).</span><span class="na">equals</span><span class="p">(</span><span class="n">passwd64</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w">                    </span><span class="n">logger</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">&quot;Unsalted SHA-1 encoded password matched for user &#39;&quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">username</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot;&#39;&quot;</span><span class="p">);</span>
<span class="w">                    </span><span class="n">authenticated</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">                </span><span class="p">}</span>
<span class="w">            </span><span class="p">}</span>
<span class="w">            </span><span class="c1">// test libc crypt() encoded password</span>
<span class="w">            </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">supportCryptPwd</span><span class="p">()</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">storedPwd</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">Crypt</span><span class="p">.</span><span class="na">crypt</span><span class="p">(</span><span class="n">passwd</span><span class="p">,</span><span class="w"> </span><span class="n">storedPwd</span><span class="p">)))</span><span class="w"> </span><span class="p">{</span>
<span class="w">                </span><span class="n">logger</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">&quot;Libc crypt encoded password matched for user &#39;&quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">username</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot;&#39;&quot;</span><span class="p">);</span>
<span class="w">                </span><span class="n">authenticated</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">            </span><span class="p">}</span>
<span class="w">            </span><span class="c1">// test clear text</span>
<span class="w">            </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">supportPlaintextPwd</span><span class="p">()</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">storedPwd</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">passwd</span><span class="p">)){</span>
<span class="w">                </span><span class="n">logger</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">&quot;Clear text password matched for user &#39;&quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">username</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot;&#39;&quot;</span><span class="p">);</span>
<span class="w">                </span><span class="n">authenticated</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">            </span><span class="p">}</span>


<span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">authenticated</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w">                </span><span class="n">logger</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">&quot;Htpasswd authenticated: &quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">username</span><span class="p">);</span>

<span class="w">                </span><span class="n">UserModel</span><span class="w"> </span><span class="n">curr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">userManager</span><span class="p">.</span><span class="na">getUserModel</span><span class="p">(</span><span class="n">username</span><span class="p">);</span>
<span class="w">                </span><span class="n">UserModel</span><span class="w"> </span><span class="n">user</span><span class="p">;</span>
<span class="w">                </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">curr</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w">                    </span><span class="c1">// create user object for new authenticated user</span>
<span class="w">                    </span><span class="n">user</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">UserModel</span><span class="p">(</span><span class="n">username</span><span class="p">);</span>
<span class="w">                </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w">                	</span><span class="n">user</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">curr</span><span class="p">;</span>
<span class="w">                </span><span class="p">}</span>

<span class="w">                </span><span class="c1">// create a user cookie</span>
<span class="w">                </span><span class="n">setCookie</span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">password</span><span class="p">);</span>

<span class="w">                </span><span class="c1">// Set user attributes, hide password from backing user service.</span>
<span class="w">                </span><span class="n">user</span><span class="p">.</span><span class="na">password</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Constants</span><span class="p">.</span><span class="na">EXTERNAL_ACCOUNT</span><span class="p">;</span>
<span class="w">                </span><span class="n">user</span><span class="p">.</span><span class="na">accountType</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getAccountType</span><span class="p">();</span>

<span class="w">                </span><span class="c1">// Push the looked up values to backing file</span>
<span class="w">               	</span><span class="n">updateUser</span><span class="p">(</span><span class="n">user</span><span class="p">);</span>

<span class="w">                </span><span class="k">return</span><span class="w"> </span><span class="n">user</span><span class="p">;</span>
<span class="w">            </span><span class="p">}</span>
<span class="w">        </span><span class="p">}</span>

<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="cm">/**</span>
<span class="cm">     * Get the account type used for this user service.</span>
<span class="cm">     *</span>
<span class="cm">     * @return AccountType.HTPASSWD</span>
<span class="cm">     */</span>
<span class="w">    </span><span class="nd">@Override</span>
<span class="w">	</span><span class="kd">public</span><span class="w"> </span><span class="n">AccountType</span><span class="w"> </span><span class="nf">getAccountType</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">AccountType</span><span class="p">.</span><span class="na">HTPASSWD</span><span class="p">;</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="cm">/**</span>
<span class="cm">     * Reads the realm file and rebuilds the in-memory lookup tables.</span>
<span class="cm">     */</span>
<span class="w">    </span><span class="kd">protected</span><span class="w"> </span><span class="kd">synchronized</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">read</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">    	</span><span class="kt">boolean</span><span class="w"> </span><span class="n">forceReload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span>
<span class="w">    	</span><span class="n">File</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getFileOrFolder</span><span class="p">(</span><span class="n">KEY_HTPASSWD_FILE</span><span class="p">,</span><span class="w"> </span><span class="n">DEFAULT_HTPASSWD_FILE</span><span class="p">);</span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">file</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">htpasswdFile</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w">            </span><span class="k">this</span><span class="p">.</span><span class="na">htpasswdFile</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">file</span><span class="p">;</span>
<span class="w">            </span><span class="k">this</span><span class="p">.</span><span class="na">htUsers</span><span class="p">.</span><span class="na">clear</span><span class="p">();</span>
<span class="w">            </span><span class="n">forceReload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span>
<span class="w">        </span><span class="p">}</span>

<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">htpasswdFile</span><span class="p">.</span><span class="na">exists</span><span class="p">()</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="p">(</span><span class="n">forceReload</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">(</span><span class="n">htpasswdFile</span><span class="p">.</span><span class="na">lastModified</span><span class="p">()</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">lastModified</span><span class="p">)))</span><span class="w"> </span><span class="p">{</span>
<span class="w">            </span><span class="n">lastModified</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">htpasswdFile</span><span class="p">.</span><span class="na">lastModified</span><span class="p">();</span>
<span class="w">            </span><span class="n">htUsers</span><span class="p">.</span><span class="na">clear</span><span class="p">();</span>

<span class="w">            </span><span class="n">Pattern</span><span class="w"> </span><span class="n">entry</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Pattern</span><span class="p">.</span><span class="na">compile</span><span class="p">(</span><span class="s">&quot;^([^:]+):(.+)&quot;</span><span class="p">);</span>

<span class="w">            </span><span class="n">Scanner</span><span class="w"> </span><span class="n">scanner</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span>
<span class="w">            </span><span class="k">try</span><span class="w"> </span><span class="p">{</span>
<span class="w">                </span><span class="n">scanner</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Scanner</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">FileInputStream</span><span class="p">(</span><span class="n">htpasswdFile</span><span class="p">));</span>
<span class="w">                </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">scanner</span><span class="p">.</span><span class="na">hasNextLine</span><span class="p">())</span><span class="w"> </span><span class="p">{</span>
<span class="w">                    </span><span class="n">String</span><span class="w"> </span><span class="n">line</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">scanner</span><span class="p">.</span><span class="na">nextLine</span><span class="p">().</span><span class="na">trim</span><span class="p">();</span>
<span class="w">                    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">line</span><span class="p">.</span><span class="na">isEmpty</span><span class="p">()</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w">  </span><span class="o">!</span><span class="n">line</span><span class="p">.</span><span class="na">startsWith</span><span class="p">(</span><span class="s">&quot;#&quot;</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w">                        </span><span class="n">Matcher</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">entry</span><span class="p">.</span><span class="na">matcher</span><span class="p">(</span><span class="n">line</span><span class="p">);</span>
<span class="w">                        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="na">matches</span><span class="p">())</span><span class="w"> </span><span class="p">{</span>
<span class="w">                            </span><span class="n">htUsers</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="na">group</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span><span class="w"> </span><span class="n">m</span><span class="p">.</span><span class="na">group</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span>
<span class="w">                        </span><span class="p">}</span>
<span class="w">                    </span><span class="p">}</span>
<span class="w">                </span><span class="p">}</span>
<span class="w">            </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">Exception</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w">                </span><span class="n">logger</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="n">MessageFormat</span><span class="p">.</span><span class="na">format</span><span class="p">(</span><span class="s">&quot;Failed to read {0}&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">htpasswdFile</span><span class="p">),</span><span class="w"> </span><span class="n">e</span><span class="p">);</span>
<span class="w">            </span><span class="p">}</span><span class="w"> </span><span class="k">finally</span><span class="w"> </span><span class="p">{</span>
<span class="w">                </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">scanner</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w">                	</span><span class="n">scanner</span><span class="p">.</span><span class="na">close</span><span class="p">();</span>
<span class="w">                </span><span class="p">}</span>
<span class="w">            </span><span class="p">}</span>
<span class="w">        </span><span class="p">}</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">supportPlaintextPwd</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">settings</span><span class="p">.</span><span class="na">getBoolean</span><span class="p">(</span><span class="n">KEY_SUPPORT_PLAINTEXT_PWD</span><span class="p">,</span><span class="w"> </span><span class="n">supportPlainTextPwd</span><span class="p">);</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">supportCryptPwd</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="o">!</span><span class="n">supportPlaintextPwd</span><span class="p">();</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="cm">/*</span>
<span class="cm">     * Method only used for unit tests. Return number of users read from htpasswd file.</span>
<span class="cm">     */</span>
<span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">getNumberHtpasswdUsers</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">htUsers</span><span class="p">.</span><span class="na">size</span><span class="p">();</span>
<span class="w">    </span><span class="p">}</span>

<span class="w">    </span><span class="nd">@Override</span>
<span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">toString</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">getClass</span><span class="p">().</span><span class="na">getSimpleName</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot;(&quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">((</span><span class="n">htpasswdFile</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="n">htpasswdFile</span><span class="p">.</span><span class="na">getAbsolutePath</span><span class="p">()</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s">&quot;null&quot;</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot;)&quot;</span><span class="p">;</span>
<span class="w">    </span><span class="p">}</span>
<span class="p">}</span>
</pre></div>
</code></pre></td></tr></table>
</div> <!-- class=content -->
<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit v1.2.3</a> (<a href='https://git-scm.com/'>git 2.39.1</a>) at 2025-08-15 02:12:52 +0000</div>
</div> <!-- id=cgit -->
</body>
</html>
