mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 07:25:44 +01:00
merge
This commit is contained in:
@@ -11,7 +11,7 @@ type State = {
|
|||||||
passwordConfirmationFailed: boolean
|
passwordConfirmationFailed: boolean
|
||||||
};
|
};
|
||||||
type Props = {
|
type Props = {
|
||||||
passwordChanged: string => void,
|
passwordChanged: (string, boolean) => void,
|
||||||
passwordValidator?: string => boolean,
|
passwordValidator?: string => boolean,
|
||||||
// Context props
|
// Context props
|
||||||
t: string => string
|
t: string => string
|
||||||
@@ -98,14 +98,12 @@ class PasswordConfirmation extends React.Component<Props, State> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
isValid = () => {
|
||||||
|
return this.state.passwordValid && !this.state.passwordConfirmationFailed
|
||||||
|
};
|
||||||
|
|
||||||
propagateChange = () => {
|
propagateChange = () => {
|
||||||
if (
|
this.props.passwordChanged(this.state.password, this.isValid());
|
||||||
this.state.password &&
|
|
||||||
this.state.passwordValid &&
|
|
||||||
!this.state.passwordConfirmationFailed
|
|
||||||
) {
|
|
||||||
this.props.passwordChanged(this.state.password);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ type State = {
|
|||||||
password: string,
|
password: string,
|
||||||
loading: boolean,
|
loading: boolean,
|
||||||
error?: Error,
|
error?: Error,
|
||||||
passwordChanged: boolean
|
passwordChanged: boolean,
|
||||||
|
passwordValid: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
class ChangeUserPassword extends React.Component<Props, State> {
|
class ChangeUserPassword extends React.Component<Props, State> {
|
||||||
@@ -35,7 +36,8 @@ class ChangeUserPassword extends React.Component<Props, State> {
|
|||||||
passwordConfirmationError: false,
|
passwordConfirmationError: false,
|
||||||
validatePasswordError: false,
|
validatePasswordError: false,
|
||||||
validatePassword: "",
|
validatePassword: "",
|
||||||
passwordChanged: false
|
passwordChanged: false,
|
||||||
|
passwordValid: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +85,10 @@ class ChangeUserPassword extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
isValid = () => {
|
||||||
|
return this.state.oldPassword && this.state.passwordValid;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { t } = this.props;
|
const { t } = this.props;
|
||||||
const { loading, passwordChanged, error } = this.state;
|
const { loading, passwordChanged, error } = this.state;
|
||||||
@@ -118,7 +124,7 @@ class ChangeUserPassword extends React.Component<Props, State> {
|
|||||||
key={this.state.passwordChanged ? "changed" : "unchanged"}
|
key={this.state.passwordChanged ? "changed" : "unchanged"}
|
||||||
/>
|
/>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={!this.state.password}
|
disabled={!this.isValid()}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
label={t("password.submit")}
|
label={t("password.submit")}
|
||||||
/>
|
/>
|
||||||
@@ -126,8 +132,8 @@ class ChangeUserPassword extends React.Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordChanged = (password: string) => {
|
passwordChanged = (password: string, passwordValid: boolean) => {
|
||||||
this.setState({ ...this.state, password });
|
this.setState({ ...this.state, password, passwordValid: (!!password && passwordValid) });
|
||||||
};
|
};
|
||||||
|
|
||||||
onClose = () => {
|
onClose = () => {
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ type State = {
|
|||||||
password: string,
|
password: string,
|
||||||
loading: boolean,
|
loading: boolean,
|
||||||
error?: Error,
|
error?: Error,
|
||||||
passwordChanged: boolean
|
passwordChanged: boolean,
|
||||||
|
passwordValid: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetUserPassword extends React.Component<Props, State> {
|
class SetUserPassword extends React.Component<Props, State> {
|
||||||
@@ -32,7 +33,8 @@ class SetUserPassword extends React.Component<Props, State> {
|
|||||||
passwordConfirmationError: false,
|
passwordConfirmationError: false,
|
||||||
validatePasswordError: false,
|
validatePasswordError: false,
|
||||||
validatePassword: "",
|
validatePassword: "",
|
||||||
passwordChanged: false
|
passwordChanged: false,
|
||||||
|
passwordValid: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +106,7 @@ class SetUserPassword extends React.Component<Props, State> {
|
|||||||
key={this.state.passwordChanged ? "changed" : "unchanged"}
|
key={this.state.passwordChanged ? "changed" : "unchanged"}
|
||||||
/>
|
/>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={!this.state.password}
|
disabled={!this.state.passwordValid}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
label={t("user-form.submit")}
|
label={t("user-form.submit")}
|
||||||
/>
|
/>
|
||||||
@@ -112,8 +114,8 @@ class SetUserPassword extends React.Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordChanged = (password: string) => {
|
passwordChanged = (password: string, passwordValid: boolean) => {
|
||||||
this.setState({ ...this.state, password });
|
this.setState({ ...this.state, password, passwordValid: (!!password && passwordValid) });
|
||||||
};
|
};
|
||||||
|
|
||||||
onClose = () => {
|
onClose = () => {
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ type State = {
|
|||||||
user: User,
|
user: User,
|
||||||
mailValidationError: boolean,
|
mailValidationError: boolean,
|
||||||
nameValidationError: boolean,
|
nameValidationError: boolean,
|
||||||
displayNameValidationError: boolean
|
displayNameValidationError: boolean,
|
||||||
|
passwordValid: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
class UserForm extends React.Component<Props, State> {
|
class UserForm extends React.Component<Props, State> {
|
||||||
@@ -41,7 +42,8 @@ class UserForm extends React.Component<Props, State> {
|
|||||||
},
|
},
|
||||||
mailValidationError: false,
|
mailValidationError: false,
|
||||||
displayNameValidationError: false,
|
displayNameValidationError: false,
|
||||||
nameValidationError: false
|
nameValidationError: false,
|
||||||
|
passwordValid: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +63,6 @@ class UserForm extends React.Component<Props, State> {
|
|||||||
|
|
||||||
isValid = () => {
|
isValid = () => {
|
||||||
const user = this.state.user;
|
const user = this.state.user;
|
||||||
const passwordValid = this.props.user ? !this.isFalsy(user.password) : true;
|
|
||||||
return !(
|
return !(
|
||||||
this.state.nameValidationError ||
|
this.state.nameValidationError ||
|
||||||
this.state.mailValidationError ||
|
this.state.mailValidationError ||
|
||||||
@@ -69,7 +70,7 @@ class UserForm extends React.Component<Props, State> {
|
|||||||
this.isFalsy(user.name) ||
|
this.isFalsy(user.name) ||
|
||||||
this.isFalsy(user.displayName) ||
|
this.isFalsy(user.displayName) ||
|
||||||
this.isFalsy(user.mail) ||
|
this.isFalsy(user.mail) ||
|
||||||
passwordValid
|
!this.state.passwordValid
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -166,9 +167,10 @@ class UserForm extends React.Component<Props, State> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handlePasswordChange = (password: string) => {
|
handlePasswordChange = (password: string, passwordValid: boolean) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
user: { ...this.state.user, password }
|
user: { ...this.state.user, password },
|
||||||
|
passwordValid: !this.isFalsy(password) && passwordValid
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import static com.google.common.base.Preconditions.*;
|
|||||||
//~--- JDK imports ------------------------------------------------------------
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -88,12 +89,17 @@ public class SecureKeyResolver extends SigningKeyResolverAdapter
|
|||||||
*/
|
*/
|
||||||
@Inject
|
@Inject
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public SecureKeyResolver(ConfigurationEntryStoreFactory storeFactory)
|
public SecureKeyResolver(ConfigurationEntryStoreFactory storeFactory) {
|
||||||
|
this(storeFactory, new SecureRandom());
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureKeyResolver(ConfigurationEntryStoreFactory storeFactory, Random random)
|
||||||
{
|
{
|
||||||
store = storeFactory
|
store = storeFactory
|
||||||
.withType(SecureKey.class)
|
.withType(SecureKey.class)
|
||||||
.withName(STORE_NAME)
|
.withName(STORE_NAME)
|
||||||
.build();
|
.build();
|
||||||
|
this.random = random;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
@@ -112,7 +118,9 @@ public class SecureKeyResolver extends SigningKeyResolverAdapter
|
|||||||
|
|
||||||
SecureKey key = store.get(subject);
|
SecureKey key = store.get(subject);
|
||||||
|
|
||||||
checkState(key != null, "could not resolve key for subject %s", subject);
|
if (key == null) {
|
||||||
|
return getSecureKey(subject).getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
return key.getBytes();
|
return key.getBytes();
|
||||||
}
|
}
|
||||||
@@ -161,7 +169,7 @@ public class SecureKeyResolver extends SigningKeyResolverAdapter
|
|||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** secure randon */
|
/** secure randon */
|
||||||
private final SecureRandom random = new SecureRandom();
|
private final Random random;
|
||||||
|
|
||||||
/** configuration entry store */
|
/** configuration entry store */
|
||||||
private final ConfigurationEntryStore<SecureKey> store;
|
private final ConfigurationEntryStore<SecureKey> store;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package sonia.scm.web.security;
|
package sonia.scm.web.security;
|
||||||
|
|
||||||
|
import org.apache.shiro.authc.AuthenticationException;
|
||||||
import org.apache.shiro.authc.AuthenticationToken;
|
import org.apache.shiro.authc.AuthenticationToken;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -64,7 +65,13 @@ public class TokenRefreshFilter extends HttpFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void examineToken(HttpServletRequest request, HttpServletResponse response, BearerToken token) {
|
private void examineToken(HttpServletRequest request, HttpServletResponse response, BearerToken token) {
|
||||||
AccessToken accessToken = resolver.resolve(token);
|
AccessToken accessToken;
|
||||||
|
try {
|
||||||
|
accessToken = resolver.resolve(token);
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
LOG.trace("could not resolve token", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (accessToken instanceof JwtAccessToken) {
|
if (accessToken instanceof JwtAccessToken) {
|
||||||
refresher.refresh((JwtAccessToken) accessToken)
|
refresher.refresh((JwtAccessToken) accessToken)
|
||||||
.ifPresent(jwtAccessToken -> refreshToken(request, response, jwtAccessToken));
|
.ifPresent(jwtAccessToken -> refreshToken(request, response, jwtAccessToken));
|
||||||
|
|||||||
@@ -44,12 +44,16 @@ import org.mockito.junit.MockitoJUnitRunner;
|
|||||||
import sonia.scm.store.ConfigurationEntryStore;
|
import sonia.scm.store.ConfigurationEntryStore;
|
||||||
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.in;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.argThat;
|
import static org.mockito.Mockito.argThat;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@@ -99,10 +103,11 @@ public class SecureKeyResolverTest
|
|||||||
* Method description
|
* Method description
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Test(expected = IllegalStateException.class)
|
@Test
|
||||||
public void testResolveSigningKeyBytesWithoutKey()
|
public void testResolveSigningKeyBytesWithoutKey()
|
||||||
{
|
{
|
||||||
resolver.resolveSigningKeyBytes(null, Jwts.claims().setSubject("test"));
|
byte[] bytes = resolver.resolveSigningKeyBytes(null, Jwts.claims().setSubject("test"));
|
||||||
|
assertThat(bytes[0]).isEqualTo((byte) 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,7 +137,9 @@ public class SecureKeyResolverTest
|
|||||||
assertThat(storeParameters.getType()).isEqualTo(SecureKey.class);
|
assertThat(storeParameters.getType()).isEqualTo(SecureKey.class);
|
||||||
return true;
|
return true;
|
||||||
}))).thenReturn(store);
|
}))).thenReturn(store);
|
||||||
resolver = new SecureKeyResolver(factory);
|
Random random = mock(Random.class);
|
||||||
|
doAnswer(invocation -> ((byte[]) invocation.getArguments()[0])[0] = 42).when(random).nextBytes(any());
|
||||||
|
resolver = new SecureKeyResolver(factory, random);
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user