package de.otto.hmac.authentication; import com.google.common.io.ByteSource; import com.google.common.io.ByteStreams; import com.google.common.io.FileBackedOutputStream; import de.otto.hmac.HmacAttributes; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.time.Clock; import java.time.Instant; public class WrappedOutputStream extends OutputStream { private final OutputStream out; private final FileBackedOutputStream tmpOut; private final WrappedOutputStreamContext cr; private final String user; private final String secretKey; private final Clock clock; public WrappedOutputStream(final String user, final String secretKey, final WrappedOutputStreamContext cr, final OutputStream out, final Clock clock) { this.out = out; tmpOut = new FileBackedOutputStream(10 * 1000 * 1000); this.cr = cr; this.user = user; this.secretKey = secretKey; this.clock = clock; } @Override public void write(byte[] b, int off, int len) throws IOException { tmpOut.write(b, off, len); } @Override public void write(byte[] b) throws IOException { tmpOut.write(b); } @Override public void write(int b) throws IOException { tmpOut.write(b); } @Override public void close() throws IOException { addHmacHttpRequestHeaders(cr, user, secretKey, Instant.now(clock), tmpOut.asByteSource()); if (tmpOut.asByteSource().isEmpty()) { // workaround for bug in jersey: without writing a single byte to the // underlying CommittingOutputStream, its method commit/commitStream will not be executed. // This is responsible to write lately added headers either via // abstract method getOutputStream() (jersey1) or // OutboundMessageContext.StreamProvider.getOutputStream() // // this call enforces a commit call and does nothing else out.write("".getBytes(), 0 , 0); } try (InputStream in = tmpOut.asByteSource().openBufferedStream()) { ByteStreams.copy(in, out); } tmpOut.close(); out.close(); } public static void addHmacHttpRequestHeaders( final WrappedOutputStreamContext cr, final String user, final String secretKey, Instant now, ByteSource body) { String signatureHeader = user + ":" + RequestSigningUtil.createRequestSignature(cr.getMethod(), now.toString(), cr.getRequestUri(), body, secretKey); cr.putSingle(HmacAttributes.X_HMAC_AUTH_SIGNATURE, signatureHeader); cr.putSingle(HmacAttributes.X_HMAC_AUTH_DATE, now.toString()); } }