064 instance_ls uptime for deleted, suspended
This commit is contained in:
+1
-1
@@ -51,7 +51,7 @@
|
||||
|
||||
//variables.framework.docs={};
|
||||
variables.framework.docs.APIName="Deck API";
|
||||
variables.framework.docs.APIVersion="0.062";
|
||||
variables.framework.docs.APIVersion="0.064";
|
||||
|
||||
variables.framework.globalHeaders = structNew();
|
||||
variables.framework.globalHeaders["Access-Control-Expose-Headers"] = "Location";
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
component /*https://www.bennadel.com/blog/2976-trying-to-generate-cryptographically-strong-random-tokens-in-coldfusion.htm*/
|
||||
output = false
|
||||
hint = "I generate random tokens using Java's SecureRandom class."
|
||||
{
|
||||
|
||||
/**
|
||||
* I initialize the token generator.
|
||||
*
|
||||
* @output false
|
||||
*/
|
||||
public any function init() {
|
||||
|
||||
// I am the generator implementation used to generate the random token data.
|
||||
// --
|
||||
// NOTE: The SHA1PRNG is not the default in all implementations of the JVM. As
|
||||
// such, we're defining the algorithm explicitly to keep this code consistent.
|
||||
generator = createObject( "java", "java.security.SecureRandom" )
|
||||
.getInstance(
|
||||
javaCast( "string", "SHA1PRNG" ),
|
||||
javaCast( "string", "SUN" )
|
||||
)
|
||||
;
|
||||
|
||||
// Now that we've initialized the random generator, we have to generate a random
|
||||
// byte of data. This will ensure that the random generator is self-seeded using
|
||||
// a shared seed generator.
|
||||
// --
|
||||
// CAUTION: Since the underlying seed generator reads from sources of entropy,
|
||||
// this may hang until enough entropy has been collected. This is another good
|
||||
// reason to do it during initialization time rather than at first-use time.
|
||||
generator.nextBytes( charsetDecode( " ", "utf-8" ) );
|
||||
|
||||
// I hold the future date at which time the generator should be reseeded in order
|
||||
// to keep it unpredictable.
|
||||
// --
|
||||
// NOTE: This doesn't affect the randomness of the values. But, the thinking
|
||||
// is that the longer the generator is producing values using the same seed,
|
||||
// the more likely an attacker is to be able to determine the original seed by
|
||||
// passively observing generated values.
|
||||
reseedAt = getNextReseedAt();
|
||||
|
||||
return( this );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---
|
||||
// PUBLIC METHODS.
|
||||
// ---
|
||||
|
||||
|
||||
/**
|
||||
* I generate "cryptographically strong" random token strings that are based on the
|
||||
* given number of random bytes. The random bytes are subsequently encoded using a
|
||||
* base64url character-set so that they are URL-safe and can be used in a variety of
|
||||
* contexts. And, since base65url is a case-sensitive schema, the tokens will
|
||||
* naturally be case-sensitive.
|
||||
*
|
||||
* @byteCount I am the number of random bytes used to generate the token.
|
||||
* @output false
|
||||
*/
|
||||
public string function nextToken( numeric byteCount = 32 ) {
|
||||
|
||||
// Check to see if the generator needs to be reseeded (using a double-check
|
||||
// locking approach to reduce the bottleneck).
|
||||
if ( now() >= reseedAt ) {
|
||||
|
||||
// Synchronize the reseeding.
|
||||
// --
|
||||
// NOTE: From what I have read, I DON'T BELIEVE that .generateSeed() will
|
||||
// ever hang with the SHA1PRNG algorithm. However, it is unclear to me. As
|
||||
// such, I'm using [throwOnTimeout = false] so that parallel threads won't
|
||||
// error if the lock cannot be obtained in a timely manner and will just
|
||||
// fall through to using the generator with the pre-seeding state.
|
||||
lock
|
||||
name = "TokenGenerator.reseedCheck"
|
||||
type = "exclusive"
|
||||
timeout = 1
|
||||
throwOnTimeout = false
|
||||
{
|
||||
|
||||
// Perform double-check - generator may have already been reseeded by
|
||||
// a parallel request.
|
||||
if ( now() >= reseedAt ) {
|
||||
|
||||
reseedAt = getNextReseedAt();
|
||||
|
||||
// NOTE: Once the generator was seeded internally, this re-seeding
|
||||
// will only ever "add to" the existing seed. As such, this is still
|
||||
// building on top of the original randomness and calling this, on
|
||||
// interval, this will never reduce randomness.
|
||||
generator.setSeed( generator.generateSeed( javaCast( "int", 32 ) ) );
|
||||
|
||||
}
|
||||
|
||||
} // END: Lock.
|
||||
|
||||
}
|
||||
|
||||
// Create the byte buffer into which the random bytes will be written. Since
|
||||
// there's no "correct" way to generate a byte array in ColdFusion, we can
|
||||
// generate a string of the desired length and then decode it into bytes.
|
||||
var byteBuffer = charsetDecode( repeatString( " ", byteCount ), "utf-8" );
|
||||
|
||||
generator.nextBytes( byteBuffer );
|
||||
|
||||
return( encodeBytes( byteBuffer ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---
|
||||
// PRIVATE METHODS.
|
||||
// ---
|
||||
|
||||
|
||||
/**
|
||||
* I encode the given byte array using the base64url character-set.
|
||||
*
|
||||
* @bytes I am the byte array being encoded.
|
||||
* @output false
|
||||
*/
|
||||
private string function encodeBytes( required binary bytes ) {
|
||||
|
||||
var token = binaryEncode( bytes, "base64" );
|
||||
|
||||
// Replace the characters that are not allowed in the base64url format. The
|
||||
// characters [+, /, =] are removed for URL-based base64 values because they
|
||||
// have significant meaning in the context of URL paths and query-strings.
|
||||
token = replace( token, "+", "-", "all" );
|
||||
token = replace( token, "/", "_", "all" );
|
||||
token = replace( token, "=", "", "all" );
|
||||
|
||||
return( token );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* I calculate the next date of reseeding.
|
||||
*
|
||||
* @output false
|
||||
*/
|
||||
private date function getNextReseedAt() {
|
||||
|
||||
return( dateAdd( "h", 1, now() ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,6 +79,7 @@
|
||||
<!--- Такая бодяга. Кастомные сериализаторы не умеют вложенных структур (поразительно, или я не нашел как). Стандартный сериализатор не умеет выводить дату в ISO 8601. Приходится колхозить и форматировать на стороне БД (насилу подобрал формат). Также на стороне БД приходится конвертировать GUID, потому что драйвер pg jdbc его представляет как структуру из 2 чисел.
|
||||
И то сказать, jsonb как-то странно сериализуется, в 3 поля, вместо одного Value
|
||||
--->
|
||||
<!--- ********* Внимание, ниже дублирование этого селекта! --->
|
||||
<cfquery name="local.qCnt" result="local.result">
|
||||
select count(*) as cnt
|
||||
from (
|
||||
@@ -111,7 +112,7 @@
|
||||
<m:field>(select to_char(io.dt_start, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as operation_dt_start</m:field>
|
||||
<m:field>(select to_char(io.dt_finish, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as operation_dt_finish</m:field>
|
||||
<m:field>(select round(extract('epoch' from coalesce(io.dt_finish,CURRENT_TIMESTAMP)- io.dt_start),1) from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as duration</m:field>
|
||||
<m:field>(select case when io.is_successful then extract('epoch' from CURRENT_TIMESTAMP - coalesce(io.dt_finish,CURRENT_TIMESTAMP)) else 0 end from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as uptime</m:field>
|
||||
<m:field>(select case when io.operation IN ('delete', 'suspend') AND io.is_successful then 0 /*null*/ when io.is_successful then extract('epoch' from CURRENT_TIMESTAMP - coalesce(io.dt_finish,CURRENT_TIMESTAMP)) else 0 end from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as uptime</m:field>/* *** это вообще неправильно, некоторые операции заканчиваются состоянием running, а некоторые нет, переделать */
|
||||
<m:field>(select to_char(io.dt_finish, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') from instance_operation io where io.instance_uid=e.instance_uid AND io.is_successful AND io.operation='create' order by dt_submit desc limit 1) as dt_deploy</m:field>
|
||||
<m:field formatter=#function(x){return (deserializeJson(x))}#>(select instance_data::text from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) as instance_data</m:field><!--- *** операция выглядит очень накладно, но можно это поле в дельнейшем не выводить --->
|
||||
<m:field>(select st.instance_state_uid::text from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) as instance_state_uid</m:field>
|
||||
@@ -188,7 +189,7 @@
|
||||
<m:field>(select to_char(io.dt_start, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as operation_dt_start</m:field>
|
||||
<m:field>(select to_char(io.dt_finish, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as operation_dt_finish</m:field>
|
||||
<m:field>(select round(extract('epoch' from coalesce(io.dt_finish,CURRENT_TIMESTAMP)- io.dt_start),1) from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as duration</m:field>
|
||||
<m:field>(select case when io.is_successful then extract('epoch' from CURRENT_TIMESTAMP - coalesce(io.dt_finish,CURRENT_TIMESTAMP)) else 0 end from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as uptime</m:field>
|
||||
<m:field>(select case when io.operation IN ('delete', 'suspend') AND io.is_successful then 0 /*null*/ when io.is_successful then extract('epoch' from CURRENT_TIMESTAMP - coalesce(io.dt_finish,CURRENT_TIMESTAMP)) else 0 end from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as uptime</m:field>/* *** это вообще неправильно, некоторые операции заканчиваются состоянием running, а некоторые нет, переделать */
|
||||
<m:field>(select to_char(io.dt_finish, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') from instance_operation io where io.instance_uid=e.instance_uid AND io.is_successful AND io.operation='create' order by dt_submit desc limit 1) as dt_deploy</m:field>
|
||||
<!--- *** не то же самое, что в DETAIL view! --->
|
||||
<m:field formatter=#function(x){return (deserializeJson(x))}#>(select instance_data::text from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) as instance_data</m:field><!--- *** операция выглядит очень накладно, но можно это поле в дельнейшем не выводить --->
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
</cffunction>
|
||||
|
||||
<!--- *** выглядит ужасно, но красивый вариант не придумывается ряд месяцев --->
|
||||
<!--- *** Внимание! нужно учитывать скоуп уникальности --->
|
||||
<cffunction name="computeDefault">
|
||||
<cfargument name="defaultCompute"/>
|
||||
<cfargument name="original"/>
|
||||
@@ -100,6 +101,12 @@
|
||||
<cfswitch expression=#arguments.defaultCompute#>
|
||||
<cfcase value="display_name">
|
||||
<cfreturn CreateObject("component", "instance_ls").generateDefaultName(arguments.service_id,arguments.usr_id)/>
|
||||
</cfcase>
|
||||
<cfcase value="append_pseudorandom">
|
||||
<!--- <cfset var generator=createObject('component', 'lib.TokenGenerator')/>
|
||||
<cfset generator.init()/> --->
|
||||
<cfset var generator = new lib.TokenGenerator()/>
|
||||
<cfreturn "#arguments.original##lcase(generator.nextToken(6))#"/> <!--- почему-то длина отличается, чтобы получить 8, пишу 6 --->
|
||||
</cfcase>
|
||||
<cfdefaultcase>
|
||||
<cfreturn #arguments.original#/>
|
||||
|
||||
Reference in New Issue
Block a user