001package org.kuali.common.util.enc;
002
003import java.util.List;
004
005import org.jasypt.util.text.TextEncryptor;
006import org.kuali.common.util.Assert;
007import org.kuali.common.util.PropertyUtils;
008import org.kuali.common.util.Str;
009import org.kuali.common.util.spring.SpringUtils;
010import org.kuali.common.util.spring.env.BasicEnvironmentService;
011import org.kuali.common.util.spring.env.EnvUtils;
012import org.kuali.common.util.spring.env.EnvironmentService;
013
014import com.google.common.base.Optional;
015import com.google.common.collect.ImmutableList;
016
017public final class EncContext {
018
019        private final Optional<TextEncryptor> textEncryptor;
020        private final EncStrength strength;
021
022        private EncContext(Builder builder) {
023                this.strength = builder.strength;
024                this.textEncryptor = builder.textEncryptor;
025        }
026
027        public Optional<TextEncryptor> getTextEncryptor() {
028                return textEncryptor;
029        }
030
031        public EncStrength getStrength() {
032                return strength;
033        }
034
035        public static Builder builder(String password) {
036                return new Builder(password);
037        }
038
039        public static Builder builder(EnvironmentService env) {
040                return new Builder(env);
041        }
042
043        public static class Builder {
044
045                // Required (but optional)
046                private final Optional<String> password;
047                private final Optional<EnvironmentService> env;
048
049                // Optional
050                private Optional<TextEncryptor> textEncryptor = Optional.absent();
051                private EncStrength strength = EncStrength.BASIC;
052                private boolean required = false;
053                private boolean removeSystemProperties = false;
054
055                private static final List<String> PASSWORD_KEYS = ImmutableList.of("enc.password", "properties.enc.password");
056                private static final List<String> STRENGTH_KEYS = ImmutableList.of("enc.strength", "properties.enc.strength");
057                private static final List<String> PASSWORD_REQUIRED_KEYS = ImmutableList.of("enc.password.required", "properties.decrypt");
058                private static final String PASSWORD_REMOVE_KEY = "enc.password.removeSystemProperty";
059
060                /**
061                 * Setup encryption using <code>password</code>
062                 */
063                public Builder(String password) {
064                        this(EnvUtils.ABSENT, Optional.of(password));
065                }
066
067                /**
068                 * Use the password they gave us, unless it is overridden by a password in the environment
069                 */
070                public Builder(EnvironmentService env, String password) {
071                        this(Optional.of(env), Optional.of(password));
072                }
073
074                /**
075                 * Use system properties / environment variables to locate the encryption password
076                 */
077                public Builder() {
078                        this(new BasicEnvironmentService());
079                }
080
081                /**
082                 * Locate the encryption password in the environment
083                 */
084                public Builder(EnvironmentService env) {
085                        this(Optional.of(env), Optional.<String> absent());
086                }
087
088                private Builder(Optional<EnvironmentService> env, Optional<String> password) {
089                        if (env.isPresent()) {
090                                this.password = SpringUtils.getString(env, PASSWORD_KEYS, password);
091                        } else {
092                                this.password = password;
093                        }
094                        this.env = env;
095                }
096
097                public Builder removeSystemProperties(boolean removeSystemProperties) {
098                        this.removeSystemProperties = removeSystemProperties;
099                        return this;
100                }
101
102                public Builder required(boolean required) {
103                        this.required = required;
104                        return this;
105                }
106
107                public Builder strength(EncStrength strength) {
108                        this.strength = strength;
109                        return this;
110                }
111
112                private void override() {
113                        if (env.isPresent()) {
114                                strength(SpringUtils.getProperty(env, STRENGTH_KEYS, EncStrength.class, strength));
115                                required(SpringUtils.getProperty(env, PASSWORD_REQUIRED_KEYS, Boolean.class, required));
116                                removeSystemProperties(env.get().getBoolean(PASSWORD_REMOVE_KEY, removeSystemProperties));
117                        }
118                }
119
120                private void validate(EncContext ctx, boolean required, Optional<String> password) {
121                        Assert.notNull(ctx.getTextEncryptor(), "'textEncryptor' cannot be null");
122                        Assert.notNull(ctx.getStrength(), "'strength' cannot be null");
123                        if (required) {
124                                Assert.isTrue(ctx.getTextEncryptor().isPresent());
125                        }
126                        if (password.isPresent()) {
127                                Assert.noBlanks(password.get());
128                                Assert.notEncrypted(password.get());
129                                Assert.notConcealed(password.get());
130                        }
131                }
132
133                private void finish() {
134                        override();
135                        if (password.isPresent()) {
136                                String revealed = Str.reveal(password.get());
137                                TextEncryptor enc = EncUtils.getTextEncryptor(revealed, strength);
138                                this.textEncryptor = Optional.of(enc);
139                        }
140                }
141
142                public EncContext build() {
143                        // Finish setting up the builder
144                        finish();
145
146                        // Get local references to builder instance variables
147                        boolean required = this.required;
148                        boolean removeSystemProperties = this.removeSystemProperties;
149                        Optional<String> password = Optional.fromNullable(this.password.orNull());
150
151                        // Construct the encryption context
152                        EncContext ctx = new EncContext(this);
153
154                        // Validate that it's in good shape
155                        validate(ctx, required, password);
156
157                        // Now that we've successfully created and validated the instance, it's safe to remove the system properties
158                        if (removeSystemProperties) {
159                                PropertyUtils.removeSystemProperties(PASSWORD_KEYS);
160                        }
161
162                        // Return the context
163                        return ctx;
164                }
165
166        }
167
168}