Skip to content

Commit a45e128

Browse files
committed
Moved ValidatedPassword to Java
1 parent 328b8c3 commit a45e128

File tree

3 files changed

+172
-45
lines changed

3 files changed

+172
-45
lines changed

logstash-core/lib/logstash/settings.rb

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -438,51 +438,51 @@ def validate(value)
438438
java_import org.logstash.settings.StringSetting
439439

440440
java_import org.logstash.settings.NullableStringSetting
441-
442441
java_import org.logstash.settings.PasswordSetting
442+
ValidatedPassword = org.logstash.settings.ValidatedPasswordSetting
443443

444-
class ValidatedPassword < Setting::PasswordSetting
445-
def initialize(name, value, password_policies)
446-
@password_policies = password_policies
447-
super(name, value, true)
448-
end
449-
450-
def coerce(password)
451-
if password && !password.kind_of?(::LogStash::Util::Password)
452-
raise(ArgumentError, "Setting `#{name}` could not coerce LogStash::Util::Password value to password")
453-
end
454-
455-
policies = build_password_policies
456-
validatedResult = LogStash::Util::PasswordValidator.new(policies).validate(password.value)
457-
if validatedResult.length() > 0
458-
if @password_policies.fetch(:mode).eql?("WARN")
459-
logger.warn("Password #{validatedResult}.")
460-
else
461-
raise(ArgumentError, "Password #{validatedResult}.")
462-
end
463-
end
464-
password
465-
end
466-
467-
def build_password_policies
468-
policies = {}
469-
policies[Util::PasswordPolicyType::EMPTY_STRING] = Util::PasswordPolicyParam.new
470-
policies[Util::PasswordPolicyType::LENGTH] = Util::PasswordPolicyParam.new("MINIMUM_LENGTH", @password_policies.dig(:length, :minimum).to_s)
471-
if @password_policies.dig(:include, :upper).eql?("REQUIRED")
472-
policies[Util::PasswordPolicyType::UPPER_CASE] = Util::PasswordPolicyParam.new
473-
end
474-
if @password_policies.dig(:include, :lower).eql?("REQUIRED")
475-
policies[Util::PasswordPolicyType::LOWER_CASE] = Util::PasswordPolicyParam.new
476-
end
477-
if @password_policies.dig(:include, :digit).eql?("REQUIRED")
478-
policies[Util::PasswordPolicyType::DIGIT] = Util::PasswordPolicyParam.new
479-
end
480-
if @password_policies.dig(:include, :symbol).eql?("REQUIRED")
481-
policies[Util::PasswordPolicyType::SYMBOL] = Util::PasswordPolicyParam.new
482-
end
483-
policies
484-
end
485-
end
444+
# class ValidatedPassword < Setting::PasswordSetting
445+
# def initialize(name, value, password_policies)
446+
# @password_policies = password_policies
447+
# super(name, value, true)
448+
# end
449+
#
450+
# def coerce(password)
451+
# if password && !password.kind_of?(::LogStash::Util::Password)
452+
# raise(ArgumentError, "Setting `#{name}` could not coerce LogStash::Util::Password value to password")
453+
# end
454+
#
455+
# policies = build_password_policies
456+
# validatedResult = LogStash::Util::PasswordValidator.new(policies).validate(password.value)
457+
# if validatedResult.length() > 0
458+
# if @password_policies.fetch(:mode).eql?("WARN")
459+
# logger.warn("Password #{validatedResult}.")
460+
# else
461+
# raise(ArgumentError, "Password #{validatedResult}.")
462+
# end
463+
# end
464+
# password
465+
# end
466+
#
467+
# def build_password_policies
468+
# policies = {}
469+
# policies[Util::PasswordPolicyType::EMPTY_STRING] = Util::PasswordPolicyParam.new
470+
# policies[Util::PasswordPolicyType::LENGTH] = Util::PasswordPolicyParam.new("MINIMUM_LENGTH", @password_policies.dig(:length, :minimum).to_s)
471+
# if @password_policies.dig(:include, :upper).eql?("REQUIRED")
472+
# policies[Util::PasswordPolicyType::UPPER_CASE] = Util::PasswordPolicyParam.new
473+
# end
474+
# if @password_policies.dig(:include, :lower).eql?("REQUIRED")
475+
# policies[Util::PasswordPolicyType::LOWER_CASE] = Util::PasswordPolicyParam.new
476+
# end
477+
# if @password_policies.dig(:include, :digit).eql?("REQUIRED")
478+
# policies[Util::PasswordPolicyType::DIGIT] = Util::PasswordPolicyParam.new
479+
# end
480+
# if @password_policies.dig(:include, :symbol).eql?("REQUIRED")
481+
# policies[Util::PasswordPolicyType::SYMBOL] = Util::PasswordPolicyParam.new
482+
# end
483+
# policies
484+
# end
485+
# end
486486

487487
# The CoercibleString allows user to enter any value which coerces to a String.
488488
# For example for true/false booleans; if the possible_strings are ["foo", "true", "false"]

logstash-core/spec/logstash/settings_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,14 @@
284284
it "raises an error when supplied value is not LogStash::Util::Password" do
285285
expect {
286286
LogStash::Setting::ValidatedPassword.new("test.validated.password", "testPassword", password_policies)
287-
}.to raise_error(ArgumentError, a_string_including("Setting `test.validated.password` could not coerce LogStash::Util::Password value to password"))
287+
}.to raise_error(IllegalArgumentException, a_string_including("Setting `test.validated.password` could not coerce LogStash::Util::Password value to password"))
288288
end
289289

290290
it "fails on validation" do
291291
password = LogStash::Util::Password.new("Password!")
292292
expect {
293293
LogStash::Setting::ValidatedPassword.new("test.validated.password", password, password_policies)
294-
}.to raise_error(ArgumentError, a_string_including("Password must contain at least one digit between 0 and 9."))
294+
}.to raise_error(IllegalArgumentException, a_string_including("Password must contain at least one digit between 0 and 9."))
295295
end
296296

297297
it "validates the password successfully" do
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.logstash.settings;
21+
22+
import co.elastic.logstash.api.Password;
23+
import org.apache.logging.log4j.LogManager;
24+
import org.apache.logging.log4j.Logger;
25+
import org.jruby.RubySymbol;
26+
import org.logstash.RubyUtil;
27+
import org.logstash.secret.password.PasswordPolicyParam;
28+
import org.logstash.secret.password.PasswordPolicyType;
29+
import org.logstash.secret.password.PasswordValidator;
30+
31+
import java.util.HashMap;
32+
import java.util.List;
33+
import java.util.Map;
34+
35+
public class ValidatedPasswordSetting extends PasswordSetting {
36+
37+
private static final Logger LOGGER = LogManager.getLogger(ValidatedPasswordSetting.class);
38+
39+
private final Map<String, Object> passwordPolicies;
40+
41+
@SuppressWarnings("this-escape")
42+
public ValidatedPasswordSetting(String name, Object defaultValue, Map<Object, Object> passwordPolicies) {
43+
super(name, defaultValue, false); // this super doesn't call validate and coerce
44+
45+
this.passwordPolicies = convertKeyRubyLabelsToStrings(passwordPolicies);
46+
Password coercedDefault = coerce(defaultValue);
47+
validate(coercedDefault);
48+
this.defaultValue = coercedDefault;
49+
}
50+
51+
@SuppressWarnings("unchecked")
52+
private Map<String, Object> convertKeyRubyLabelsToStrings(Map<Object, Object> passwordPolicies) {
53+
Map<String, Object> result = new HashMap<>();
54+
for (Map.Entry<Object, Object> entry : passwordPolicies.entrySet()) {
55+
final String transformedKey = entry.getKey().toString();;
56+
Object value = entry.getValue();
57+
if (value instanceof Map) {
58+
value = convertKeyRubyLabelsToStrings((Map<Object, Object>) value);
59+
}
60+
// TODO handle lists if needed ?
61+
// TODO what if the value is a RubySymbol ?
62+
result.put(transformedKey, value);
63+
}
64+
return result;
65+
}
66+
67+
@Override
68+
public Password coerce(Object password) {
69+
if (password != null && !(password instanceof Password)) {
70+
throw new IllegalArgumentException("Setting `" + getName() + "` could not coerce LogStash::Util::Password value to password");
71+
}
72+
LOGGER.info("Password policies: {}", passwordPolicies);
73+
System.out.printf("Password policies: %s%n", passwordPolicies);
74+
Map<PasswordPolicyType, PasswordPolicyParam> policies = buildPasswordPolicies();
75+
String validatedResult = new PasswordValidator(policies).validate(((Password) password).getValue());
76+
77+
if (!validatedResult.isEmpty()) {
78+
if ("WARN".equals(passwordPolicies.get("mode"))) {
79+
LOGGER.warn("Password {}.", validatedResult);
80+
} else {
81+
throw new IllegalArgumentException("Password "+ validatedResult + ".");
82+
}
83+
}
84+
return (Password) password;
85+
}
86+
87+
private Map<PasswordPolicyType, PasswordPolicyParam> buildPasswordPolicies() {
88+
Map<PasswordPolicyType, PasswordPolicyParam> policies = new HashMap<>();
89+
90+
policies.put(PasswordPolicyType.EMPTY_STRING, new PasswordPolicyParam());
91+
Object minLengthPolicyValue = dig(passwordPolicies, "length", "minimum");
92+
93+
// in Ruby "nil.to_s" is "", so we do the same here
94+
policies.put(PasswordPolicyType.LENGTH, new PasswordPolicyParam("MINIMUM_LENGTH",
95+
minLengthPolicyValue != null? minLengthPolicyValue.toString() : ""));
96+
97+
if ("REQUIRED".equals(dig(passwordPolicies, "include", "upper"))) {
98+
policies.put(PasswordPolicyType.UPPER_CASE, new PasswordPolicyParam());
99+
}
100+
if ("REQUIRED".equals(dig(passwordPolicies, "include", "lower"))) {
101+
policies.put(PasswordPolicyType.LOWER_CASE, new PasswordPolicyParam());
102+
}
103+
if ("REQUIRED".equals(dig(passwordPolicies, "include", "digit"))) {
104+
policies.put(PasswordPolicyType.DIGIT, new PasswordPolicyParam());
105+
}
106+
if ("REQUIRED".equals(dig(passwordPolicies, "include", "symbol"))) {
107+
policies.put(PasswordPolicyType.SYMBOL, new PasswordPolicyParam());
108+
}
109+
return policies;
110+
}
111+
112+
private static Object dig(Map<String, Object> map, String... path) {
113+
Object current = map;
114+
for (String key : path) {
115+
if (!(current instanceof Map)) {
116+
return null;
117+
}
118+
current = ((Map<?, ?>) current).get(key);
119+
if (current == null) {
120+
return null;
121+
}
122+
}
123+
return current;
124+
}
125+
126+
127+
}

0 commit comments

Comments
 (0)