Skip to content

Commit 61da02c

Browse files
committed
Moved ValidatedPassword to Java
1 parent 44c3a7c commit 61da02c

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
@@ -436,51 +436,51 @@ def validate(value)
436436
java_import org.logstash.settings.StringSetting
437437

438438
java_import org.logstash.settings.NullableStringSetting
439-
440439
java_import org.logstash.settings.PasswordSetting
440+
ValidatedPassword = org.logstash.settings.ValidatedPasswordSetting
441441

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

485485
# The CoercibleString allows user to enter any value which coerces to a String.
486486
# 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)