Skip to content

Commit df5bb22

Browse files
committed
provides full implementation of AuthMetadataFlyweight
Signed-off-by: Oleh Dokuka <[email protected]>
1 parent c127f4b commit df5bb22

File tree

4 files changed

+430
-9
lines changed

4 files changed

+430
-9
lines changed

rsocket-core/src/main/java/io/rsocket/buffer/AbstractTupleByteBuf.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ public int writeCharSequence(CharSequence sequence, Charset charset) {
550550

551551
@Override
552552
public ByteBuffer internalNioBuffer(int index, int length) {
553-
throw new UnsupportedOperationException();
553+
return nioBuffer(index, length);
554554
}
555555

556556
@Override

rsocket-core/src/main/java/io/rsocket/metadata/security/AuthMetadataFlyweight.java

Lines changed: 186 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import io.netty.buffer.ByteBuf;
44
import io.netty.buffer.ByteBufAllocator;
55
import io.netty.buffer.ByteBufUtil;
6+
import io.netty.buffer.Unpooled;
7+
import io.netty.util.CharsetUtil;
68
import io.rsocket.buffer.TupleByteBuf;
79
import io.rsocket.util.CharByteBufUtil;
810

@@ -14,6 +16,8 @@ public class AuthMetadataFlyweight {
1416
static final int USERNAME_BYTES_LENGTH = 1;
1517
static final int AUTH_TYPE_ID_LENGTH = 1;
1618

19+
static final char[] EMPTY_CHARS_ARRAY = new char[0];
20+
1721
private AuthMetadataFlyweight() {}
1822

1923
/**
@@ -80,7 +84,7 @@ public static ByteBuf encodeMetadata(
8084
/**
8185
* Encode a Authentication CompositeMetadata payload using Simple Authentication format
8286
*
83-
* @throws IllegalArgumentException if username length is greater than 128
87+
* @throws IllegalArgumentException if username length is greater than 256
8488
* @param allocator the {@link ByteBufAllocator} to use to create intermediate buffers as needed.
8589
* @param username the char sequence which represents user name.
8690
* @param password the char sequence which represents user password.
@@ -89,9 +93,9 @@ public static ByteBuf encodeSimpleMetadata(
8993
ByteBufAllocator allocator, char[] username, char[] password) {
9094

9195
int usernameLength = CharByteBufUtil.utf8Bytes(username);
92-
if (usernameLength > 128) {
96+
if (usernameLength > 256) {
9397
throw new IllegalArgumentException(
94-
"Username should be shorter than or equal to 128 bytes length in UTF-8 encoding");
98+
"Username should be shorter than or equal to 256 bytes length in UTF-8 encoding");
9599
}
96100

97101
int passwordLength = CharByteBufUtil.utf8Bytes(password);
@@ -152,4 +156,183 @@ public static ByteBuf encodeMetadataWithCompression(
152156
return AuthMetadataFlyweight.encodeMetadata(allocator, wkn, metadata);
153157
}
154158
}
159+
160+
/**
161+
* Get the first {@code byte} from a {@link ByteBuf} and check whether it is length or {@link
162+
* WellKnownAuthType}. Assuming said buffer properly contains such a {@code byte}
163+
*
164+
* @param metadata byteBuf used to get information from
165+
*/
166+
public static boolean isWellKnownAuthType(ByteBuf metadata) {
167+
byte lengthOrId = metadata.getByte(0);
168+
return (lengthOrId & STREAM_METADATA_LENGTH_MASK) != lengthOrId;
169+
}
170+
171+
/**
172+
* Read first byte from the given {@code metadata} and tries to convert it's value to {@link
173+
* WellKnownAuthType}.
174+
*
175+
* @param metadata given metadata buffer to read from
176+
* @return Return on of the know Auth types or {@link WellKnownAuthType#UNPARSEABLE_AUTH_TYPE} if
177+
* field's value is length or unknown auth type
178+
* @throws IllegalStateException if not enough readable bytes in the given ByteBuf
179+
*/
180+
public static WellKnownAuthType decodeWellKnownAuthType(ByteBuf metadata) {
181+
if (metadata.readableBytes() < 1) {
182+
throw new IllegalStateException(
183+
"Unable to decode Well Know Auth type. Not enough readable bytes");
184+
}
185+
byte lengthOrId = metadata.readByte();
186+
int normalizedId = (byte) (lengthOrId & STREAM_METADATA_LENGTH_MASK);
187+
188+
if (normalizedId != lengthOrId) {
189+
return WellKnownAuthType.fromIdentifier(normalizedId);
190+
}
191+
192+
return WellKnownAuthType.UNPARSEABLE_AUTH_TYPE;
193+
}
194+
195+
/**
196+
* Read up to 129 bytes from the given metadata in order to get the custom Auth Type
197+
*
198+
* @param metadata
199+
* @return
200+
*/
201+
public static CharSequence decodeCustomAuthType(ByteBuf metadata) {
202+
if (metadata.readableBytes() < 2) {
203+
throw new IllegalStateException(
204+
"Unable to decode custom Auth type. Not enough readable bytes");
205+
}
206+
207+
byte encodedLength = metadata.readByte();
208+
if (encodedLength < 0) {
209+
throw new IllegalStateException(
210+
"Unable to decode custom Auth type. Incorrect auth type length");
211+
}
212+
213+
// encoded length is realLength - 1 in order to avoid intersection with 0x00 authtype
214+
int realLength = encodedLength + 1;
215+
if (metadata.readableBytes() < realLength) {
216+
throw new IllegalArgumentException(
217+
"Unable to decode custom Auth type. Malformed length or auth type string");
218+
}
219+
220+
return metadata.readCharSequence(realLength, CharsetUtil.US_ASCII);
221+
}
222+
223+
/**
224+
* Read all remaining {@code bytes} from the given {@link ByteBuf} and return sliced
225+
* representation of a payload
226+
*
227+
* @param metadata metadata to get payload from. Please note, the {@code metadata#readIndex}
228+
* should be set to the beginning of the payload bytes
229+
* @return sliced {@link ByteBuf} or {@link Unpooled#EMPTY_BUFFER} if no bytes readable in the
230+
* given one
231+
*/
232+
public static ByteBuf decodePayload(ByteBuf metadata) {
233+
if (metadata.readableBytes() == 0) {
234+
return Unpooled.EMPTY_BUFFER;
235+
}
236+
237+
return metadata.readSlice(metadata.readableBytes());
238+
}
239+
240+
/**
241+
* Read up to 257 {@code bytes} from the given {@link ByteBuf} where the first byte is username
242+
* length and the subsequent number of bytes equal to decoded length
243+
*
244+
* @param simpleAuthMetadata the given metadata to read username from. Please note, the {@code
245+
* simpleAuthMetadata#readIndex} should be set to the username length byte
246+
* @return sliced {@link ByteBuf} or {@link Unpooled#EMPTY_BUFFER} if username length is zero
247+
*/
248+
public static ByteBuf decodeUsername(ByteBuf simpleAuthMetadata) {
249+
short usernameLength = decodeUsernameLength(simpleAuthMetadata);
250+
251+
if (usernameLength == 0) {
252+
return Unpooled.EMPTY_BUFFER;
253+
}
254+
255+
return simpleAuthMetadata.readSlice(usernameLength);
256+
}
257+
258+
/**
259+
* Read all the remaining {@code byte}s from the given {@link ByteBuf} which represents user's
260+
* password
261+
*
262+
* @param simpleAuthMetadata the given metadata to read password from. Please note, the {@code
263+
* simpleAuthMetadata#readIndex} should be set to the beginning of the password bytes
264+
* @return sliced {@link ByteBuf} or {@link Unpooled#EMPTY_BUFFER} if password length is zero
265+
*/
266+
public static ByteBuf decodePassword(ByteBuf simpleAuthMetadata) {
267+
if (simpleAuthMetadata.readableBytes() == 0) {
268+
return Unpooled.EMPTY_BUFFER;
269+
}
270+
271+
return simpleAuthMetadata.readSlice(simpleAuthMetadata.readableBytes());
272+
}
273+
/**
274+
* Read up to 257 {@code bytes} from the given {@link ByteBuf} where the first byte is username
275+
* length and the subsequent number of bytes equal to decoded length
276+
*
277+
* @param simpleAuthMetadata the given metadata to read username from. Please note, the {@code
278+
* simpleAuthMetadata#readIndex} should be set to the username length byte
279+
* @return {@code char[]} which represents UTF-8 username
280+
*/
281+
public static char[] decodeUsernameAsCharArray(ByteBuf simpleAuthMetadata) {
282+
short usernameLength = decodeUsernameLength(simpleAuthMetadata);
283+
284+
if (usernameLength == 0) {
285+
return EMPTY_CHARS_ARRAY;
286+
}
287+
288+
return CharByteBufUtil.readUtf8(simpleAuthMetadata, usernameLength);
289+
}
290+
291+
/**
292+
* Read all the remaining {@code byte}s from the given {@link ByteBuf} which represents user's
293+
* password
294+
*
295+
* @param simpleAuthMetadata the given metadata to read username from. Please note, the {@code
296+
* simpleAuthMetadata#readIndex} should be set to the beginning of the password bytes
297+
* @return {@code char[]} which represents UTF-8 password
298+
*/
299+
public static char[] decodePasswordAsCharArray(ByteBuf simpleAuthMetadata) {
300+
if (simpleAuthMetadata.readableBytes() == 0) {
301+
return EMPTY_CHARS_ARRAY;
302+
}
303+
304+
return CharByteBufUtil.readUtf8(simpleAuthMetadata, simpleAuthMetadata.readableBytes());
305+
}
306+
307+
/**
308+
* Read all the remaining {@code bytes} from the given {@link ByteBuf} where the first byte is
309+
* username length and the subsequent number of bytes equal to decoded length
310+
*
311+
* @param bearerAuthMetadata the given metadata to read username from. Please note, the {@code
312+
* simpleAuthMetadata#readIndex} should be set to the beginning of the password bytes
313+
* @return {@code char[]} which represents UTF-8 password
314+
*/
315+
public static char[] decodeBearerTokenAsCharArray(ByteBuf bearerAuthMetadata) {
316+
if (bearerAuthMetadata.readableBytes() == 0) {
317+
return EMPTY_CHARS_ARRAY;
318+
}
319+
320+
return CharByteBufUtil.readUtf8(bearerAuthMetadata, bearerAuthMetadata.readableBytes());
321+
}
322+
323+
private static short decodeUsernameLength(ByteBuf simpleAuthMetadata) {
324+
if (simpleAuthMetadata.readableBytes() < 1) {
325+
throw new IllegalStateException(
326+
"Unable to decode custom username. Not enough readable bytes");
327+
}
328+
329+
short usernameLength = simpleAuthMetadata.readUnsignedByte();
330+
331+
if (simpleAuthMetadata.readableBytes() < usernameLength) {
332+
throw new IllegalArgumentException(
333+
"Unable to decode username. Malformed username length or content");
334+
}
335+
336+
return usernameLength;
337+
}
155338
}

rsocket-core/src/main/java/io/rsocket/util/CharByteBufUtil.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33
import static io.netty.util.internal.StringUtil.isSurrogate;
44

55
import io.netty.buffer.ByteBuf;
6+
import io.netty.util.CharsetUtil;
67
import io.netty.util.internal.MathUtil;
8+
import java.nio.ByteBuffer;
9+
import java.nio.CharBuffer;
10+
import java.nio.charset.CharacterCodingException;
11+
import java.nio.charset.CharsetDecoder;
12+
import java.nio.charset.CoderResult;
13+
import java.util.Arrays;
714

815
public class CharByteBufUtil {
916

@@ -168,4 +175,34 @@ private static int writeUtf8Surrogate(ByteBuf buffer, int writerIndex, char c, c
168175
buffer.setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f)));
169176
return writerIndex;
170177
}
178+
179+
public static char[] readUtf8(ByteBuf byteBuf, int length) {
180+
CharsetDecoder charsetDecoder = CharsetUtil.UTF_8.newDecoder();
181+
int en = (int) (length * (double) charsetDecoder.maxCharsPerByte());
182+
char[] ca = new char[en];
183+
184+
CharBuffer charBuffer = CharBuffer.wrap(ca);
185+
ByteBuffer byteBuffer = byteBuf.internalNioBuffer(byteBuf.readerIndex(), length);
186+
byteBuffer.mark();
187+
try {
188+
CoderResult cr = charsetDecoder.decode(byteBuffer, charBuffer, true);
189+
if (!cr.isUnderflow()) cr.throwException();
190+
cr = charsetDecoder.flush(charBuffer);
191+
if (!cr.isUnderflow()) cr.throwException();
192+
193+
byteBuffer.reset();
194+
byteBuf.skipBytes(length);
195+
196+
return safeTrim(charBuffer.array(), charBuffer.position());
197+
} catch (CharacterCodingException x) {
198+
// Substitution is always enabled,
199+
// so this shouldn't happen
200+
throw new IllegalStateException("unable to decode char array from the given buffer", x);
201+
}
202+
}
203+
204+
private static char[] safeTrim(char[] ca, int len) {
205+
if (len == ca.length) return ca;
206+
else return Arrays.copyOf(ca, len);
207+
}
171208
}

0 commit comments

Comments
 (0)