All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.finos.legend.sdlc.server.auth.Token Maven / Gradle / Ivy

There is a newer version: 0.177.6
Show newest version
// Copyright 2020 Goldman Sachs
//
// 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 org.finos.legend.sdlc.server.auth;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;

public class Token
{
    private static final int NULL_STRING_CODE = 0b00000000;
    private static final int EMPTY_STRING_CODE = 0b10000000;

    private static final int LOWER_HEX_STRING_CODE = 0b00000001;
    private static final int UPPER_HEX_STRING_CODE = 0b00000010;

    private static final int BYTE_LENGTH_CODE = 0b01000000;
    private static final int SHORT_LENGTH_CODE = 0b00100000;
    private static final int INT_LENGTH_CODE = 0b00010000;

    private static final byte BOOLEAN_TRUE = 1;
    private static final byte BOOLEAN_FALSE = 0;

    private static final Encoder ENCODER = Base64.getUrlEncoder().withoutPadding();
    private static final Decoder DECODER = Base64.getUrlDecoder();
    private static final Charset STRING_CHARSET = StandardCharsets.UTF_8;
    private static final Charset TOKEN_CHARSET = StandardCharsets.ISO_8859_1;

    public static class TokenBuilder
    {
        private final byte[] eightBytes = new byte[8];
        private final ByteBuffer eightByteBuffer = ByteBuffer.wrap(this.eightBytes);
        private final ByteArrayOutputStream stream;

        private TokenBuilder(int initialSize)
        {
            this.stream = new ByteArrayOutputStream(initialSize);
        }

        public synchronized String toTokenString()
        {
            return new String(ENCODER.encode(this.stream.toByteArray()), TOKEN_CHARSET);
        }

        public synchronized TokenBuilder putBoolean(boolean b)
        {
            return putByte(b ? BOOLEAN_TRUE : BOOLEAN_FALSE);
        }

        public synchronized TokenBuilder putByte(byte b)
        {
            this.stream.write(b);
            return this;
        }

        public synchronized TokenBuilder putShort(short s)
        {
            this.eightByteBuffer.putShort(0, s);
            return putBytes(this.eightBytes, 0, 2);
        }

        public synchronized TokenBuilder putInt(int i)
        {
            this.eightByteBuffer.putInt(0, i);
            return putBytes(this.eightBytes, 0, 4);
        }

        public synchronized TokenBuilder putLong(long l)
        {
            this.eightByteBuffer.putLong(0, l);
            return putBytes(this.eightBytes, 0, 8);
        }

        public synchronized TokenBuilder putString(String string)
        {
            if (string == null)
            {
                putByte((byte) NULL_STRING_CODE);
                return this;
            }

            if (string.isEmpty())
            {
                putByte((byte) EMPTY_STRING_CODE);
                return this;
            }

            int code = 0;

            // Get the string bytes
            byte[] bytes;
            int stringTypes = getStringTypes(string);
            if ((stringTypes & LOWER_HEX_STRING_CODE) == LOWER_HEX_STRING_CODE)
            {
                try
                {
                    bytes = Hex.decodeHex(string.toCharArray());
                    code |= LOWER_HEX_STRING_CODE;
                }
                catch (DecoderException ignore)
                {
                    // should never happen, but just in case ...
                    bytes = string.getBytes(STRING_CHARSET);
                    code &= ~LOWER_HEX_STRING_CODE;
                }
            }
            else if ((stringTypes & UPPER_HEX_STRING_CODE) == UPPER_HEX_STRING_CODE)
            {
                try
                {
                    bytes = Hex.decodeHex(string.toCharArray());
                    code |= UPPER_HEX_STRING_CODE;
                }
                catch (DecoderException ignore)
                {
                    // should never happen, but just in case ...
                    bytes = string.getBytes(STRING_CHARSET);
                    code &= ~UPPER_HEX_STRING_CODE;
                }
            }
            else
            {
                bytes = string.getBytes(STRING_CHARSET);
            }

            // Check the length of the byte array
            int length = bytes.length;
            if ((length & 0xffffff00) == 0)
            {
                code |= BYTE_LENGTH_CODE;
            }
            else if ((length & 0xffff0000) == 0)
            {
                code |= SHORT_LENGTH_CODE;
            }
            else
            {
                code |= INT_LENGTH_CODE;
            }

            // Write code
            putByte((byte) code);

            // Write length
            if ((code & BYTE_LENGTH_CODE) != 0)
            {
                putByte((byte) length);
            }
            else if ((code & SHORT_LENGTH_CODE) != 0)
            {
                putShort((short) length);
            }
            else
            {
                putInt(length);
            }

            // Write bytes
            return putBytes(bytes, 0, length);
        }

        private synchronized TokenBuilder putBytes(byte[] bytes, int offset, int length)
        {
            this.stream.write(bytes, offset, length);
            return this;
        }

        @Override
        public String toString()
        {
            return "";
        }
    }

    public static class TokenReader
    {
        private final ByteBuffer buffer;

        private TokenReader(ByteBuffer buffer)
        {
            this.buffer = buffer;
        }

        public synchronized boolean hasRemaining()
        {
            return this.buffer.hasRemaining();
        }

        public synchronized boolean getBoolean()
        {
            byte b = getByte();
            switch (b)
            {
                case BOOLEAN_TRUE:
                {
                    return true;
                }
                case BOOLEAN_FALSE:
                {
                    return false;
                }
                default:
                {
                    throw new RuntimeException("Expected " + BOOLEAN_FALSE + " or " + BOOLEAN_TRUE + ", got: " + b);
                }
            }
        }

        public synchronized byte getByte()
        {
            return this.buffer.get();
        }

        public synchronized short getShort()
        {
            return this.buffer.getShort();
        }

        public synchronized int getInt()
        {
            return this.buffer.getInt();
        }

        public synchronized long getLong()
        {
            return this.buffer.getLong();
        }

        public synchronized String getString()
        {
            int code = this.buffer.get() & 0xff;
            if (code == NULL_STRING_CODE)
            {
                return null;
            }
            if (code == EMPTY_STRING_CODE)
            {
                return "";
            }

            int length;
            if ((code & BYTE_LENGTH_CODE) != 0)
            {
                length = getByte() & 0xff;
            }
            else if ((code & SHORT_LENGTH_CODE) != 0)
            {
                length = getShort() & 0xffff;
            }
            else
            {
                length = getInt();
            }

            if ((code & LOWER_HEX_STRING_CODE) != 0)
            {
                byte[] bytes = new byte[length];
                this.buffer.get(bytes);
                return new String(Hex.encodeHex(bytes, true));
            }
            if ((code & UPPER_HEX_STRING_CODE) != 0)
            {
                byte[] bytes = new byte[length];
                this.buffer.get(bytes);
                return new String(Hex.encodeHex(bytes, false));
            }

            int offset = this.buffer.position();
            String string = new String(this.buffer.array(), offset, length, STRING_CHARSET);
            this.buffer.position(offset + length);
            return string;
        }
    }

    public static TokenBuilder newBuilder()
    {
        return newBuilder(128);
    }

    public static TokenBuilder newBuilder(int initialSize)
    {
        return new TokenBuilder(initialSize);
    }

    public static TokenReader newReader(String token)
    {
        byte[] decodedBytes = DECODER.decode(token.getBytes(TOKEN_CHARSET));
        return new TokenReader(ByteBuffer.wrap(decodedBytes));
    }

    private static int getStringTypes(String string)
    {
        int length = string.length();
        if ((length & 0x01) != 0)
        {
            return 0;
        }

        int code = LOWER_HEX_STRING_CODE | UPPER_HEX_STRING_CODE;
        for (int i = 0; (code != 0) && (i < length); i++)
        {
            char c = string.charAt(i);
            if (((code & LOWER_HEX_STRING_CODE) == LOWER_HEX_STRING_CODE) && !isLowerHexChar(c))
            {
                code &= ~LOWER_HEX_STRING_CODE;
            }
            if (((code & UPPER_HEX_STRING_CODE) == UPPER_HEX_STRING_CODE) && !isUpperHexChar(c))
            {
                code &= ~UPPER_HEX_STRING_CODE;
            }
        }
        return code;
    }

    private static boolean isLowerHexChar(char c)
    {
        return (('0' <= c) && (c <= '9')) || (('a' <= c) && (c <= 'f'));
    }

    private static boolean isUpperHexChar(char c)
    {
        return (('0' <= c) && (c <= '9')) || (('A' <= c) && (c <= 'F'));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy