1 module itsdangerous.timed; 2 3 import std.stdio; 4 import std.datetime; 5 import std.datetime.systime; 6 import std.algorithm.searching; 7 import std.format; 8 import std.json; 9 10 import itsdangerous.dsigner; 11 import itsdangerous.encoding; 12 import itsdangerous.serializer; 13 import itsdangerous.exc; 14 15 class TimestampSigner(DigestMethod, AlgDigestMethod) : Signer!(DigestMethod, AlgDigestMethod) { 16 /+ 17 Works like the regular :class:`.Signer` but also records the time 18 of the signing and can be used to expire signatures. The 19 :meth:`unsign` method can raise :exc:`.SignatureExpired` if the 20 unsigning failed because the signature is expired. 21 +/ 22 this(string secretKey, 23 string salt = "itsdangerous.Signer", 24 char sep = '.', 25 string keyDerivation = "django-concat" 26 ){ 27 super(secretKey, salt, sep, keyDerivation); 28 } 29 30 final int getTimestamp(){ 31 /+Returns the current timestamp. The function must return an 32 integer. 33 +/ 34 return cast(int)Clock.currTime.toUnixTime(); 35 } 36 37 final DateTime timestampToDatetime(int ts){ 38 /+ 39 Used to convert the timestamp from :meth:`get_timestamp` into 40 a datetime object. 41 +/ 42 return cast(DateTime)SysTime.fromUnixTime(ts); 43 } 44 45 override string sign(string value){ 46 /+ Signs the given string and also attaches time information. +/ 47 string timestamp = base64Encode!(ubyte[])(intToBytes(getTimestamp())); 48 string _value = value ~ sep ~ timestamp; 49 return _value ~ sep ~ this.Signer.getSignature(_value); 50 } 51 52 override string unsign(string value, int maxAge = 0, int* tstamp = null){ 53 BadSignature sigError; 54 string result; 55 56 try { 57 result = this.Signer.unsign(value); 58 } catch (BadSignature ex) { 59 sigError = new BadSignature("unsign method (of super class Signer) cannot unsign timed data!"); 60 result = ""; 61 } 62 if(!canFind(result, sep)){ 63 if (sigError !is null) 64 throw sigError; 65 auto bts = new BadTimeSignature("timestamp missing"); 66 bts.payload = result; 67 throw bts; 68 } 69 import std.array; 70 auto arr = result.rsplit(sep); 71 string _value = arr[0]; 72 string timestamp = arr[1]; 73 74 int timestampInt; 75 try{ 76 timestampInt = bytesToInt(cast(ubyte[])base64Decode(timestamp)); 77 } catch (Exception ex) { 78 throw new Exception("cannot convert bytes to int"); 79 } 80 81 if (sigError !is null){ 82 auto bts = new BadTimeSignature(sigError.msg); 83 bts.payload = _value; 84 bts.dateSignedStr = timestamp; 85 throw bts; 86 } 87 88 if(timestamp is null){ 89 auto bts = new BadTimeSignature("Malformed timestamp"); 90 bts.payload = _value; 91 throw bts; 92 } 93 94 if (maxAge != 0){ 95 int age = getTimestamp() - timestampInt; 96 if (age > maxAge){ 97 auto sigExpired = new SignatureExpired(format("Signature age %s > %s seconds", age, maxAge)); 98 sigExpired.payload = value; 99 sigExpired.dateSignedStr = timestampToDatetime(timestampInt).toSimpleString; 100 throw sigExpired; 101 } 102 } 103 104 if(tstamp !is null) 105 *tstamp = timestampInt; 106 107 return _value; 108 } 109 110 final bool validate(string signedValue, int maxAge){ 111 /+ Only validates the given signed value. Returns ``True`` if 112 the signature exists and is valid.+/ 113 try{ 114 unsign(signedValue, maxAge); 115 return true; 116 } catch (BadSignature ex) { 117 return false; 118 } 119 } 120 } 121 122 class TimedSerializer(SignerType) : Serializer!SignerType { 123 /*Uses :class:`TimestampSigner` instead of the default 124 :class:`.Signer`. 125 */ 126 127 this(string secretKey, string salt = "itsdangerous"){ 128 super(secretKey, salt); 129 } 130 131 override JSONValue loads(string s, string salt = null, int maxAge = 0, int *tstamp = null){ 132 /*Reverse of :meth:`dumps`, raises :exc:`.BadSignature` if the 133 signature validation fails. If a ``max_age`` is provided it will 134 ensure the signature is not older than that time in seconds. In 135 case the signature is outdated, :exc:`.SignatureExpired` is 136 raised. All arguments are forwarded to the signer's 137 :meth:`~TimestampSigner.unsign` method. 138 */ 139 Exception lastException = null; 140 int timestamp; 141 string base64d; 142 if(signer is null) 143 signer = makeSigner(this.salt); 144 try{ 145 base64d = signer.unsign(s, maxAge, ×tamp); 146 auto payload = loadPayload(base64d); 147 if (tstamp !is null){ 148 *tstamp = timestamp; 149 } 150 return payload; 151 152 } catch (SignatureExpired ex){ 153 throw new SignatureExpired(ex.msg); 154 } catch (BadTimeSignature ex3){ 155 lastException = new BadTimeSignature(ex3.msg); 156 } catch (BadSignature ex2){ 157 lastException = new BadSignature(ex2.msg); 158 } 159 throw lastException; 160 } 161 162 /+ ? 163 def loads_unsafe(self, s, max_age=None, salt=None): 164 load_kwargs = {"max_age": max_age} 165 load_payload_kwargs = {} 166 return self._loads_unsafe_impl(s, salt, load_kwargs, load_payload_kwargs) 167 +/ 168 }