{"size": 4112, "ext": "js", "max_stars_repo_path": "src/CatenisApiAuthDynValue.js", "max_stars_repo_name": "blockchainofthings/Paw-CatenisApiAuthDynValue", "max_stars_repo_stars_event_min_datetime": "2020-07-29T17:56:58.000Z", "max_stars_repo_stars_event_max_datetime": "2020-07-29T17:56:58.000Z", "max_issues_repo_path": "src/CatenisApiAuthDynValue.js", "max_issues_repo_name": "blockchainofthings/Paw-CatenisApiAuthDynValue", "max_issues_repo_issues_event_min_datetime": "2020-04-06T15:08:23.000Z", "max_issues_repo_issues_event_max_datetime": "2021-05-10T02:09:32.000Z", "max_forks_repo_path": "src/CatenisApiAuthDynValue.js", "max_forks_repo_name": "blockchainofthings/Paw-CatenisApiAuthDynValue", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": 1, "max_issues_count": 3, "max_forks_count": null, "avg_line_length": 35.7565217391, "max_line_length": 108, "alphanum_fraction": 0.6303501946}
/**
 * Created by claudio on 25/02/17.
 */

import crypto from 'crypto';
import url from 'url';
import moment from 'moment';

@registerDynamicValueClass
class CatenisApiAuthDynValue {

    static identifier = 'com.blockchainofthings.PawExtensions.CatenisApiAuthDynValue';
    static title = 'Catenis API Authentication';
    static inputs = [
        DynamicValueInput('deviceId', 'Device ID', "String", {persisted: true}),
        DynamicValueInput('apiAccessSecret', 'API Access Secret', "SecureValue", {persisted: true})
    ];

    evaluate(context) {
        return this.signRequest(context.getCurrentRequest());
    }

    title(context) {
        return 'Catenis API Authentication';
    }

    text(context) {
        return 'For device ' + this.deviceId;
    }

    signRequest(request) {
        const signVersionId = 'CTN1',
            signMethodId = 'CTN1-HMAC-SHA256',
            scopeRequest = 'ctn1_request',
            timestampHdr = 'X-BCoT-Timestamp';

        let timestamp = request.getHeaderByName(timestampHdr);
        let tmstmp = moment(timestamp);

        if (!tmstmp.isValid()) {
            timestamp = moment.utc().format('YYYYMMDDTHHmmss[Z]');
            tmstmp = moment(timestamp);
        }
        const signDate = tmstmp.utc().format('YYYYMMDD');

        const urlHostAndPath = CatenisApiAuthDynValue.getUrlHostAndPath(request.url);

        // First step: compute conformed request
        let confReq = request.method + '\n';
        confReq += urlHostAndPath.path + '\n';

        let essentialHeaders = 'host:' + urlHostAndPath.host + '\n';
        essentialHeaders += timestampHdr.toLowerCase() + ':' + request.getHeaderByName(timestampHdr) + '\n';

        confReq += essentialHeaders + '\n';
        confReq += CatenisApiAuthDynValue.hashData(CatenisApiAuthDynValue.getRequestBody()) + '\n';
        // DEBUG - Begin
        console.log('Conformed request: ' + confReq);
        // DEBUG - End

        // Second step: assemble string to sign
        let strToSign = signMethodId + '\n';
        strToSign += timestamp + '\n';

        const scope = signDate + '/' + scopeRequest;

        strToSign += scope + '\n';
        strToSign += CatenisApiAuthDynValue.hashData(confReq) + '\n';
        // DEBUG - Begin
        console.log('String to sign: ' + strToSign);
        // DEBUG - End

        // Third step: generate the signature
        const initKey = signVersionId + this.apiAccessSecret,
            dateKey = CatenisApiAuthDynValue.signData(signDate, initKey),
            signKey = CatenisApiAuthDynValue.signData(scopeRequest, dateKey),
            signature = CatenisApiAuthDynValue.signData(strToSign, signKey, true);
        // DEBUG - Begin
        console.log('Initialization key: ' + initKey);
        console.log('Date key: ' + dateKey.toString('hex'));
        console.log('Sign key: ' + signKey.toString('hex'));
        console.log('Signature: ' + signature);
        // DEBUG - End

        return signMethodId + ' Credential=' + this.deviceId + '/' + scope + ', Signature=' + signature;
    }

    static getUrlHostAndPath(fullUrl) {
        const parsedUrl = url.parse(fullUrl),
            schemeLength = parsedUrl.protocol.length + (parsedUrl.slashes ? 2 : 0);

        return {
            host: fullUrl.substr(schemeLength, parsedUrl.host.length),
            path: fullUrl.substr(schemeLength + parsedUrl.host.length)
        };
    }

    static hashData(data) {
        return crypto.createHash('sha256').update(data).digest('hex');
    }

    static signData(data, secret, hexEncode = false) {
        return crypto.createHmac('sha256', secret).update(data).digest(hexEncode ? 'hex' : undefined);
    }

    // Method used to retrieve request body as a buffer (containing any binary data)
    static getRequestBody() {
        const dvBase64Enc = DynamicValue('com.luckymarmot.Base64EncodingDynamicValue');
        dvBase64Enc.mode = 0;  // Encode
        dvBase64Enc.input = DynamicString(DynamicValue('com.luckymarmot.RequestRawBodyDynamicValue'));

        return Buffer.from(dvBase64Enc.getEvaluatedString(), 'base64');
    }
}
