| /* |
| * Copyright 2017 The Mifos Initiative. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package io.mifos.anubis.test.v1; |
| |
| import com.google.gson.Gson; |
| import com.google.gson.GsonBuilder; |
| import io.jsonwebtoken.*; |
| import io.mifos.anubis.annotation.AcceptedTokenType; |
| import io.mifos.anubis.api.v1.RoleConstants; |
| import io.mifos.anubis.api.v1.TokenConstants; |
| import io.mifos.anubis.api.v1.domain.AllowedOperation; |
| import io.mifos.anubis.api.v1.domain.TokenContent; |
| import io.mifos.anubis.api.v1.domain.TokenPermission; |
| import io.mifos.anubis.provider.InvalidKeyTimestampException; |
| import io.mifos.anubis.provider.SystemRsaKeyProvider; |
| import io.mifos.anubis.security.SystemAuthenticator; |
| import io.mifos.anubis.service.PermittableService; |
| import io.mifos.anubis.token.SystemAccessTokenSerializer; |
| import io.mifos.anubis.token.TenantAccessTokenSerializer; |
| import io.mifos.core.api.context.AutoSeshat; |
| import io.mifos.core.api.context.AutoUserContext; |
| import io.mifos.core.api.util.ApiConstants; |
| import io.mifos.core.api.util.UserContextHolder; |
| import io.mifos.core.lang.ApplicationName; |
| import io.mifos.core.lang.TenantContextHolder; |
| import io.mifos.core.lang.security.RsaKeyPairFactory; |
| import org.mockito.Mockito; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.security.PrivateKey; |
| import java.security.PublicKey; |
| import java.security.interfaces.RSAPrivateKey; |
| import java.security.interfaces.RSAPublicKey; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.TimeUnit; |
| import java.util.stream.Collectors; |
| |
| /** |
| * @author Myrle Krantz |
| */ |
| @SuppressWarnings({"WeakerAccess", "unused"}) |
| public class SystemSecurityEnvironment { |
| final static String LOGGER_NAME = "anubis-test-logger"; |
| |
| private final TenantAccessTokenSerializer tenantAccessTokenSerializer; |
| private final SystemAccessTokenSerializer systemAccessTokenSerializer; |
| private final String systemKeyTimestamp; |
| private final PublicKey systemPublicKey; |
| private final PrivateKey systemPrivateKey; |
| private final Map<String, RsaKeyPairFactory.KeyPairHolder> tenantKeyPairHolders; |
| |
| public SystemSecurityEnvironment(final String systemKeyTimestamp, final PublicKey systemPublicKey, final PrivateKey systemPrivateKey) { |
| final Gson gson = new GsonBuilder().create(); |
| this.tenantAccessTokenSerializer = new TenantAccessTokenSerializer(gson); |
| this.systemAccessTokenSerializer = new SystemAccessTokenSerializer(); |
| this.systemKeyTimestamp = systemKeyTimestamp; |
| this.systemPublicKey = systemPublicKey; |
| this.systemPrivateKey = systemPrivateKey; |
| |
| this.tenantKeyPairHolders = new HashMap<>(); |
| } |
| |
| public AutoUserContext createAutoSystemContext(final String applicationName) |
| { |
| return new AutoSeshat(systemToken(applicationName)); |
| } |
| |
| public AutoUserContext createAutoSystemContext(final String tenantName, final String applicationName) { |
| return new AutoSeshat(systemToken(tenantName, applicationName)); |
| } |
| |
| public String systemToken(final String applicationName) { |
| return systemToken(TenantContextHolder.checkedGetIdentifier(), applicationName); |
| } |
| |
| private String systemToken(final String tenantName, final String applicationName) { |
| return systemAccessTokenSerializer.build(new SystemAccessTokenSerializer.Specification() |
| .setTenant(tenantName) |
| .setRole(RoleConstants.SYSTEM_ADMIN_ROLE_IDENTIFIER) |
| .setSecondsToLive(TimeUnit.HOURS.toSeconds(12)) |
| .setKeyTimestamp(systemKeyTimestamp) |
| .setPrivateKey(systemPrivateKey) |
| .setTargetApplicationName(applicationName) |
| ).getToken(); |
| } |
| |
| public AutoUserContext createAutoUserContext(final String userName, final List<String> applicationNames) |
| { |
| return new AutoUserContext(userName, getEverythingToken(userName, applicationNames)); |
| } |
| |
| private String getEverythingToken(final String userName, final List<String> applicationNames) |
| { |
| return tenantAccessTokenSerializer.build(new TenantAccessTokenSerializer.Specification() |
| .setUser(userName) |
| .setTokenContent(getTokenContentForStarEndpoint(applicationNames)) |
| .setSecondsToLive(TimeUnit.HOURS.toSeconds(10)) |
| .setKeyTimestamp(tenantKeyTimestamp()) |
| .setPrivateKey(tenantPrivateKey())).getToken(); |
| } |
| |
| public String getPermissionToken( |
| final String userName, |
| final String applicationName, |
| final String uri, |
| final AllowedOperation allowedOperation) { |
| return tenantAccessTokenSerializer.build( |
| new TenantAccessTokenSerializer.Specification().setPrivateKey(tenantPrivateKey()) |
| .setKeyTimestamp(tenantKeyTimestamp()) |
| .setSecondsToLive(100) |
| .setUser(userName) |
| .setTokenContent(generateOnePermissionTokenContent(applicationName, uri, allowedOperation)) |
| ).getToken(); |
| } |
| |
| private TokenContent generateOnePermissionTokenContent(final String applicationName, final String uri, final AllowedOperation allowedOperation) { |
| final TokenPermission tokenPermission |
| = new TokenPermission(applicationName + uri, Collections.singleton(allowedOperation)); |
| |
| return new TokenContent(Collections.singletonList(tokenPermission)); |
| } |
| |
| public String tenantKeyTimestamp() { |
| return tenantKeyPairHolders.computeIfAbsent(TenantContextHolder.checkedGetIdentifier(), |
| x -> RsaKeyPairFactory.createKeyPair()).getTimestamp(); |
| } |
| |
| public RSAPublicKey tenantPublicKey() |
| { |
| return tenantKeyPairHolders.computeIfAbsent(TenantContextHolder.checkedGetIdentifier(), |
| x -> RsaKeyPairFactory.createKeyPair()).publicKey(); |
| } |
| |
| public RSAPrivateKey tenantPrivateKey() |
| { |
| return tenantKeyPairHolders.computeIfAbsent(TenantContextHolder.checkedGetIdentifier(), |
| x -> RsaKeyPairFactory.createKeyPair()).privateKey(); |
| } |
| |
| private TokenContent getTokenContentForStarEndpoint(final List<String> applicationNames) { |
| return new TokenContent( |
| applicationNames.stream() |
| .map(x -> new TokenPermission(x + "/*", AllowedOperation.ALL)) |
| .collect(Collectors.toList())); |
| } |
| |
| public boolean isValidGuestSecurityContext(final String forTenant) { |
| final boolean validTenant = TenantContextHolder.identifier().map(x -> x.equals(forTenant)).orElse(false); |
| final boolean validUser = UserContextHolder.checkedGetUser().equals(RoleConstants.GUEST_USER_IDENTIFIER); |
| final boolean validToken = UserContextHolder.checkedGetAccessToken().equals(TokenConstants.NO_AUTHENTICATION); |
| |
| return validTenant && validUser && validToken; |
| } |
| |
| |
| public boolean isValidSystemSecurityContext(final String forService, final String forServiceVersion, final String forTenant) { |
| final boolean validTenant = TenantContextHolder.identifier().map(x -> x.equals(forTenant)).orElse(false); |
| final boolean validUser = UserContextHolder.checkedGetUser().equals(ApiConstants.SYSTEM_SU); |
| final boolean validToken = isValidToken( |
| UserContextHolder.checkedGetAccessToken(), |
| forService, |
| forServiceVersion, |
| UserContextHolder.checkedGetUser()); |
| |
| return validTenant && validUser && validToken; |
| } |
| |
| public boolean isValidToken(final String token, |
| final String forService, |
| final String forServiceVersion, |
| final String forUser) { |
| if (!token.startsWith(TokenConstants.PREFIX)) |
| return false; |
| |
| final String jwtToken = token.substring(TokenConstants.PREFIX.length()).trim(); |
| |
| final PermittableService permittableService = Mockito.mock(PermittableService.class); |
| Mockito.doReturn(Collections.emptySet()).when(permittableService).getPermittableEndpointsAsPermissions(AcceptedTokenType.SYSTEM); |
| |
| final SystemRsaKeyProvider systemRsaKeyProvider = Mockito.mock(SystemRsaKeyProvider.class); |
| try { |
| Mockito.doReturn(systemPublicKey).when(systemRsaKeyProvider).getPublicKey(Mockito.anyString()); |
| } |
| catch (final InvalidKeyTimestampException ignored) {} |
| |
| final Logger logger = LoggerFactory.getLogger(LOGGER_NAME); |
| |
| final SystemAuthenticator systemAuthenticator = new SystemAuthenticator( |
| systemRsaKeyProvider, |
| ApplicationName.appNameWithVersion(forService, forServiceVersion), |
| permittableService, |
| logger); |
| try { |
| return (systemAuthenticator.authenticate(forUser, jwtToken, systemKeyTimestamp) != null); |
| } |
| catch (final Exception e) |
| { |
| return false; |
| } |
| } |
| |
| public static TokenContent getTokenContent(final String token, final PublicKey publicKey) |
| { |
| final String strippedAccessToken = token.substring("Bearer ".length()); |
| |
| final JwtParser parser = Jwts.parser().setSigningKey(publicKey); |
| |
| @SuppressWarnings("unchecked") final Jwt<Header, Claims> jwt = parser.parse(strippedAccessToken); |
| |
| final String serializedTokenContent = jwt.getBody().get(TokenConstants.JWT_CONTENT_CLAIM, String.class); |
| return new Gson().fromJson(serializedTokenContent, TokenContent.class); |
| } |
| } |