/******************************************************************************* * Cloud Foundry * Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. * * This product includes a number of subcomponents with * separate copyright notices and license terms. Your use of these * subcomponents is subject to the terms and conditions of the * subcomponent's license, as noted in the LICENSE file. *******************************************************************************/ package org.cloudfoundry.identity.uaa.audit.event; import com.fasterxml.jackson.core.type.TypeReference; import org.cloudfoundry.identity.uaa.audit.AuditEvent; import org.cloudfoundry.identity.uaa.audit.AuditEventType; import org.cloudfoundry.identity.uaa.audit.UaaAuditService; import org.cloudfoundry.identity.uaa.oauth.UaaOauth2Authentication; import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper; import org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.springframework.context.ApplicationEvent; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.jwt.Jwt; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; import java.security.Principal; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import static org.cloudfoundry.identity.uaa.util.UaaTokenUtils.isJwtToken; import static org.springframework.util.StringUtils.hasText; /** * Base class for UAA events that want to publish audit records. * * @author Luke Taylor * @author Dave Syer * */ public abstract class AbstractUaaEvent extends ApplicationEvent { private static final long serialVersionUID = -7639844193401892160L; private transient final IdentityZone identityZone = IdentityZoneHolder.get(); private Authentication authentication; protected AbstractUaaEvent(Object source) { super(source); if (source instanceof Authentication) { this.authentication = (Authentication)source; } } protected AbstractUaaEvent(Object source, Authentication authentication) { super(source); this.authentication = authentication; } public void process(UaaAuditService auditor) { auditor.log(getAuditEvent()); } protected AuditEvent createAuditRecord(String principalId, AuditEventType type, String origin) { return new AuditEvent(type, principalId, origin, null, System.currentTimeMillis(), identityZone.getId()); } protected AuditEvent createAuditRecord(String principalId, AuditEventType type, String origin, String data) { return new AuditEvent(type, principalId, origin, data, System.currentTimeMillis(), identityZone.getId()); } public Authentication getAuthentication() { return authentication; } // Ideally we want to get to the point where details is never null, but this // isn't currently possible // due to some OAuth authentication scenarios which don't set it. protected String getOrigin(Principal principal) { if (principal instanceof Authentication) { Authentication caller = (Authentication) principal; StringBuilder builder = new StringBuilder(); if (caller instanceof OAuth2Authentication) { OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) caller; builder.append("client=").append(oAuth2Authentication.getOAuth2Request().getClientId()); if (!oAuth2Authentication.isClientOnly()) { builder.append(", ").append("user=").append(oAuth2Authentication.getName()); } } else { builder.append("caller=").append(caller.getName()); } if (caller.getDetails() != null) { builder.append(", details=("); try { @SuppressWarnings("unchecked") Map<String, Object> map = JsonUtils.readValue((String)caller.getDetails(), new TypeReference<Map<String,Object>>(){}); if (map.containsKey("remoteAddress")) { builder.append("remoteAddress=").append(map.get("remoteAddress")).append(", "); } builder.append("type=").append(caller.getDetails().getClass().getSimpleName()); } catch (Exception e) { // ignore builder.append(caller.getDetails()); } appendTokenDetails(caller, builder); builder.append(")"); } return builder.toString(); } return principal == null ? null : principal.getName(); } protected void appendTokenDetails(Authentication caller, StringBuilder builder) { String tokenValue = null; if (caller instanceof UaaOauth2Authentication) { tokenValue = ((UaaOauth2Authentication)caller).getTokenValue(); } else if (caller.getDetails() instanceof OAuth2AuthenticationDetails) { tokenValue = ((OAuth2AuthenticationDetails)authentication.getDetails()).getTokenValue(); } if (hasText(tokenValue)) { if (isJwtToken(tokenValue)) { try { Jwt token = JwtHelper.decode(tokenValue); Map<String, Object> claims = JsonUtils.readValue(token.getClaims(), new TypeReference<Map<String, Object>>() { }); String issuer = claims.get(ClaimConstants.ISS).toString(); String subject = claims.get(ClaimConstants.SUB).toString(); builder.append(", sub=").append(subject).append(", ").append("iss=").append(issuer); } catch (Exception e) { builder.append(", <token extraction failed>"); } } else { builder.append(", opaque-token=present"); } } } public abstract AuditEvent getAuditEvent(); protected static Authentication getContextAuthentication() { Authentication a = SecurityContextHolder.getContext().getAuthentication(); if (a==null) { a = new Authentication() { private static final long serialVersionUID = 1748694836774597624L; ArrayList<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public Object getCredentials() { return null; } @Override public Object getDetails() { return null; } @Override public Object getPrincipal() { return "null"; } @Override public boolean isAuthenticated() { return false; } @Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { } @Override public String getName() { return "null"; } }; } return a; } public IdentityZone getIdentityZone() { return identityZone; } }