1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 module hunt.shiro.util.SimpleByteSource; 20 21 import hunt.shiro.util.ByteSource; 22 import hunt.shiro.codec.Base64; 23 import hunt.shiro.codec.Hex; 24 // import hunt.shiro.util.Hex; 25 26 import hunt.Exceptions; 27 import std.array; 28 29 // import hunt.shiro.codec.Base64; 30 // import hunt.shiro.codec.CodecSupport; 31 // import hunt.shiro.codec.Hex; 32 33 // import java.io.File; 34 // import java.io.InputStream; 35 // import java.util.Arrays; 36 37 /** 38 * Very simple {@link ByteSource ByteSource} implementation that maintains an internal {@code byte[]} array and uses the 39 * {@link Hex Hex} and {@link Base64 Base64} codec classes to support the 40 * {@link #toHex() toHex()} and {@link #toBase64() toBase64()} implementations. 41 * <p/> 42 * The constructors on this class accept the following implicit byte-backed data types and will convert them to 43 * a byte-array automatically: 44 * <ul> 45 * <li>byte[]</li> 46 * <li>char[]</li> 47 * <li>string</li> 48 * <li>{@link ByteSource ByteSource}</li> 49 * <li>{@link File File}</li> 50 * <li>{@link InputStream InputStream}</li> 51 * </ul> 52 * 53 * @since 1.0 54 */ 55 class SimpleByteSource : ByteSource { 56 57 private byte[] bytes; 58 private string cachedHex; 59 private string cachedBase64; 60 61 this(byte[] bytes) { 62 this.bytes = bytes; 63 } 64 65 /** 66 * Creates an instance by converting the characters to a byte array (assumes UTF-8 encoding). 67 * 68 * @param chars the source characters to use to create the underlying byte array. 69 * @since 1.1 70 */ 71 this(char[] chars) { 72 this.bytes = cast(byte[])(chars); 73 } 74 75 /** 76 * Creates an instance by converting the string to a byte array (assumes UTF-8 encoding). 77 * 78 * @param string the source string to convert to a byte array (assumes UTF-8 encoding). 79 * @since 1.1 80 */ 81 this(string string) { 82 this.bytes = cast(byte[])(string); 83 } 84 85 /** 86 * Creates an instance using the sources bytes directly - it does not create a copy of the 87 * argument's byte array. 88 * 89 * @param source the source to use to populate the underlying byte array. 90 * @since 1.1 91 */ 92 this(ByteSource source) { 93 this.bytes = source.getBytes(); 94 } 95 96 /** 97 * Creates an instance by converting the file to a byte array. 98 * 99 * @param file the file from which to acquire bytes. 100 * @since 1.1 101 */ 102 // this(File file) { 103 // this.bytes = new BytesHelper().getBytes(file); 104 // } 105 106 /** 107 * Creates an instance by converting the stream to a byte array. 108 * 109 * @param stream the stream from which to acquire bytes. 110 * @since 1.1 111 */ 112 // this(InputStream stream) { 113 // this.bytes = new BytesHelper().getBytes(stream); 114 // } 115 116 /** 117 * Returns {@code true} if the specified object is a recognized data type that can be easily converted to 118 * bytes by instances of this class, {@code false} otherwise. 119 * <p/> 120 * This implementation returns {@code true} IFF the specified object is an instance of one of the following 121 * types: 122 * <ul> 123 * <li>{@code byte[]}</li> 124 * <li>{@code char[]}</li> 125 * <li>{@link ByteSource}</li> 126 * <li>{@link string}</li> 127 * <li>{@link File}</li> 128 * </li>{@link InputStream}</li> 129 * </ul> 130 * 131 * @param o the object to test to see if it can be easily converted to bytes by instances of this class. 132 * @return {@code true} if the specified object can be easily converted to bytes by instances of this class, 133 * {@code false} otherwise. 134 * @since 1.2 135 */ 136 static bool isCompatible(Object o) { 137 // return o instanceof byte[] || o instanceof char[] || o instanceof string || 138 // o instanceof ByteSource || o instanceof File || o instanceof InputStream; 139 implementationMissing(false); 140 return false; 141 } 142 143 144 145 byte[] getBytes() { 146 return this.bytes; 147 } 148 149 bool isEmpty() { 150 return this.bytes is null || this.bytes.length == 0; 151 } 152 153 string toHex() { 154 if ( this.cachedHex is null ) { 155 this.cachedHex = Hex.encodeToString(getBytes()); 156 } 157 return this.cachedHex; 158 } 159 160 string toBase64() { 161 if ( this.cachedBase64 is null ) { 162 this.cachedBase64 = Base64.encodeToString(getBytes()); 163 } 164 return this.cachedBase64; 165 } 166 167 override string toString() { 168 return toBase64(); 169 } 170 171 override size_t toHash() @trusted nothrow { 172 if (this.bytes.empty) { 173 return 0; 174 } 175 return hashOf(this.bytes); 176 } 177 178 override bool opEquals(Object o) { 179 if (o is this) { 180 return true; 181 } 182 ByteSource bs = cast(ByteSource) o; 183 if (o !is null) { 184 return getBytes() == bs.getBytes(); 185 } 186 return false; 187 } 188 189 //will probably be removed in Shiro 2.0. See SHIRO-203: 190 //https://issues.apache.org/jira/browse/SHIRO-203 191 // private static final class BytesHelper extends CodecSupport { 192 // byte[] getBytes(File file) { 193 // return toBytes(file); 194 // } 195 196 // byte[] getBytes(InputStream stream) { 197 // return toBytes(stream); 198 // } 199 // } 200 } 201 202 203 /** 204 * Utility class that can construct ByteSource instances. This is slightly nicer than needing to know the 205 * {@code ByteSource} implementation class to use. 206 * 207 * @since 1.2 208 */ 209 final class ByteSourceUtil { 210 211 /** 212 * Returns a new {@code ByteSource} instance representing the specified byte array. 213 * 214 * @param bytes the bytes to represent as a {@code ByteSource} instance. 215 * @return a new {@code ByteSource} instance representing the specified byte array. 216 */ 217 static ByteSource bytes(byte[] bytes) { 218 return new SimpleByteSource(bytes); 219 } 220 221 /** 222 * Returns a new {@code ByteSource} instance representing the specified character array's bytes. The byte 223 * array is obtained assuming {@code UTF-8} encoding. 224 * 225 * @param chars the character array to represent as a {@code ByteSource} instance. 226 * @return a new {@code ByteSource} instance representing the specified character array's bytes. 227 */ 228 static ByteSource bytes(char[] chars) { 229 return new SimpleByteSource(chars); 230 } 231 232 /** 233 * Returns a new {@code ByteSource} instance representing the specified string's bytes. The byte 234 * array is obtained assuming {@code UTF-8} encoding. 235 * 236 * @param string the string to represent as a {@code ByteSource} instance. 237 * @return a new {@code ByteSource} instance representing the specified string's bytes. 238 */ 239 static ByteSource bytes(string string) { 240 return new SimpleByteSource(string); 241 } 242 243 /** 244 * Returns a new {@code ByteSource} instance representing the specified ByteSource. 245 * 246 * @param source the ByteSource to represent as a new {@code ByteSource} instance. 247 * @return a new {@code ByteSource} instance representing the specified ByteSource. 248 */ 249 static ByteSource bytes(ByteSource source) { 250 return new SimpleByteSource(source); 251 } 252 253 /** 254 * Returns a new {@code ByteSource} instance representing the specified File's bytes. 255 * 256 * @param file the file to represent as a {@code ByteSource} instance. 257 * @return a new {@code ByteSource} instance representing the specified File's bytes. 258 */ 259 // static ByteSource bytes(File file) { 260 // return new SimpleByteSource(file); 261 // } 262 263 /** 264 * Returns a new {@code ByteSource} instance representing the specified InputStream's bytes. 265 * 266 * @param stream the InputStream to represent as a {@code ByteSource} instance. 267 * @return a new {@code ByteSource} instance representing the specified InputStream's bytes. 268 */ 269 // static ByteSource bytes(InputStream stream) { 270 // return new SimpleByteSource(stream); 271 // } 272 273 /** 274 * Returns {@code true} if the specified object can be easily represented as a {@code ByteSource} using 275 * the {@link ByteSourceUtil}'s default heuristics, {@code false} otherwise. 276 * <p/> 277 * This implementation merely returns {@link SimpleByteSource}.{@link SimpleByteSource#isCompatible(Object) isCompatible(source)}. 278 * 279 * @param source the object to test to see if it can be easily converted to ByteSource instances using default 280 * heuristics. 281 * @return {@code true} if the specified object can be easily represented as a {@code ByteSource} using 282 * the {@link ByteSourceUtil}'s default heuristics, {@code false} otherwise. 283 */ 284 static bool isCompatible(Object source) { 285 return SimpleByteSource.isCompatible(source); 286 } 287 288 /** 289 * Returns a {@code ByteSource} instance representing the specified byte source argument. If the argument 290 * <em>cannot</em> be easily converted to bytes (as is indicated by the {@link #isCompatible(Object)} JavaDoc), 291 * this method will throw an {@link IllegalArgumentException}. 292 * 293 * @param source the byte-backed instance that should be represented as a {@code ByteSource} instance. 294 * @return a {@code ByteSource} instance representing the specified byte source argument. 295 * @throws IllegalArgumentException if the argument <em>cannot</em> be easily converted to bytes 296 * (as indicated by the {@link #isCompatible(Object)} JavaDoc) 297 */ 298 static ByteSource bytes(Object source) { 299 if (source is null) { 300 return null; 301 } 302 implementationMissing(false); 303 return null; 304 // if (!isCompatible(source)) { 305 // string msg = "Unable to heuristically acquire bytes for object of type [" ~ 306 // source.getClass().getName() ~ "]. If this type is indeed a byte-backed data type, you might " ~ 307 // "want to write your own ByteSource implementation to extract its bytes explicitly."; 308 // throw new IllegalArgumentException(msg); 309 // } 310 // if (source instanceof byte[]) { 311 // return bytes((byte[]) source); 312 // } else if (source instanceof ByteSource) { 313 // return (ByteSource) source; 314 // } else if (source instanceof char[]) { 315 // return bytes((char[]) source); 316 // } else if (source instanceof string) { 317 // return bytes((string) source); 318 // } else if (source instanceof File) { 319 // return bytes((File) source); 320 // } else if (source instanceof InputStream) { 321 // return bytes((InputStream) source); 322 // } else { 323 // throw new IllegalStateException("Encountered unexpected byte source. This is a bug - please notify " ~ 324 // "the Shiro developer list asap (the isCompatible implementation does not reflect this " ~ 325 // "method's implementation)."); 326 // } 327 } 328 }