All files / src/token RoleToken.js

98.1% Statements 103/105
89.47% Branches 51/57
87.5% Functions 7/8
98.1% Lines 103/105
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242                              3x 3x 3x 3x       60x   60x   60x 60x 60x 60x   60x 24x 24x   6x     36x 36x   18x                       27x   27x 3x                                               24x 24x 24x     24x 24x 24x 282x 282x 282x   24x 24x   12x 12x   12x   21x 21x   24x 24x   24x 24x   24x 24x   24x 24x   24x 24x   21x 21x   24x 24x   24x 24x   12x 12x   24x 24x                     24x 3x     21x 3x     18x   18x   18x                                   36x 9x     21x 3x       18x 18x 18x 18x 18x 18x     18x 18x 18x 18x   18x   18x 18x 18x 18x 18x 3x   18x 18x   18x 18x   18x 6x   18x 18x 18x 18x 18x 18x   18x 18x       15x       12x       9x       9x       3x  
/**
 * 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.
 */
'use strict';
 
var winston = require('winston');
var Token = require('./Token');
var Crypto = require('../util/Crypto');
var config = require('../../config/config')();
 
class RoleToken extends Token {
  constructor(token) {
    super();
 
    winston.level = config.logLevel;
 
    this._roles = null;
    this._principal = null;
    this._proxyUser = null;
    this._domainCompleteRoleSet = null;
 
    if (typeof token === 'string') {
      try {
        this.parseSignedToken(token);
      } catch (e) {
        throw e;
      }
    } else {
      try {
        this.builder(token);
      } catch (e) {
        throw e;
      }
    }
  }
 
  static setConfig(c) {
    super.setConfig(c);
    config = Object.assign({}, config, c.auth_core);
  }
 
  /*eslint complexity: ["error", 24]*/
  parseSignedToken(signedToken) {
    winston.debug('Constructing RoleToken with input string: ' + signedToken);
 
    if (!signedToken) {
      throw new Error('Input String signedToken must not be empty');
    }
 
    /**
     * first we need to extract data and signature parts
     * the signature is always at the end of the token.
     * The format for the Token is as follows:
     *
     * v=Z1;d=sports;r=role1,role2;a=salt;t=tstamp;e=expiry;k=1;s=sig
     *
     * v: version number Z1 (string)
     * d: domain name where the roles are valid for
     * r: list of comma separated roles
     * c: the list of roles is complete in domain
     * p: principal that got the token issued for
     * a: random 8 byte salt value hex encoded
     * t: timestamp when the token was generated
     * h: host that issued this role token
     * e: expiry timestamp based on SIA configuration
     * k: identifier - either version or zone name
     * s: signature generated over the "v=Z1;a=salt;...;e=expiry" string
     *    using Service's private Key and y64 encoded
     * proxy: request was done by this authorized proxy user
     */
    var idx = signedToken.indexOf(';s=');
    Eif (idx !== -1) {
      this._unsignedToken = signedToken.substring(0, idx);
    }
 
    var roleNames = null;
    var item = signedToken.split(';');
    for (var i = 0; i < item.length; i++) {
      var kv = item[i].split('=');
      Eif (kv.length === 2) {
        switch (kv[0]) {
          case 'a':
            this._salt = kv[1];
            break;
          case 'c':
            Eif (Number(kv[1]) === 1) {
              this._domainCompleteRoleSet = true;
            }
            break;
          case 'd':
            this._domain = kv[1];
            break;
          case 'e':
            this._expiryTime = Number(kv[1]);
            break;
          case 'h':
            this._host = kv[1];
            break;
          case 'i':
            this._ip = kv[1];
            break;
          case 'k':
            this._keyId = kv[1];
            break;
          case 'p':
            this._principal = kv[1];
            break;
          case 'r':
            roleNames = kv[1];
            break;
          case 's':
            this._signature = kv[1];
            break;
          case 't':
            this._timestamp = Number(kv[1]);
            break;
          case 'proxy':
            this._proxyUser = kv[1];
            break;
          case 'v':
            this._version = kv[1];
            break;
        }
      }
    }
 
    /* the required attributes for the token are
     * domain and roles. The signature will be verified
     * during the authenticate phase but now we'll make
     * sure that domain and roles are present
     */
 
    if (!this._domain) {
      throw new Error('SignedToken does not contain required domain component');
    }
 
    if (!roleNames) {
      throw new Error('SignedToken does not contain required roles component');
    }
 
    this._roles = roleNames.split(',');
 
    this._signedToken = signedToken;
 
    winston.debug(
      'Values extracted from token ' +
      ' version:' + this._version +
      ' domain:' + this._domain +
      ' roles:' + roleNames +
      ' principal:' + this._principal +
      ' host:' + this._host +
      ' salt:' + this._salt +
      ' timestamp:' + this._timestamp +
      ' expiryTime:' + this._expiryTime +
      ' domainCompleteRoleSet: ' + this.domainCompleteRoleSet +
      ' keyId: ' + this._keyId +
      ' ip: ' + this._ip +
      ' proxyUser: ' + this._proxyUser +
      ' signature:' + this._signature);
  }
 
  builder(options) {
    if (!options.version || !options.domain || !options.roles) {
      throw new Error('version, domain and roles parameters must not be null.');
    }
 
    if (options.version.length === 0 || options.domain.length === 0 || options.roles.length === 0) {
      throw new Error('version, domain and roles parameters must have values.');
    }
 
    // required attributes
    this._version = options.version;
    this._domain = options.domain;
    this._roles = options.roles;
    this._principal = options.principal;
    this._proxyUser = options.proxyUser;
    this._domainCompleteRoleSet = (options.domainCompleteRoleSet || false);
 
    // optional attributes with default values
    this._salt = (options.salt || Crypto.randomSalt());
    this._host = options.host;
    this._ip = options.ip;
    this._keyId = (options.keyId || '0');
 
    this.setTimeStamp((options.issueTime || 0), (options.expirationWindow || 3600));
 
    var parts = [];
    parts.push('v=' + this._version);
    parts.push('d=' + this._domain);
    parts.push('r=' + this._roles.join(','));
    if (this._domainCompleteRoleSet) {
      parts.push("c=1");
    }
    Eif (this._principal) {
      parts.push("p=" + this._principal);
    }
    Eif (this._host) {
      parts.push('h=' + this._host);
    }
    if (this._proxyUser) {
      parts.push('proxy=' + this._proxyUser);
    }
    parts.push('a=' + this._salt);
    parts.push('t=' + this._timestamp);
    parts.push('e=' + this._expiryTime);
    parts.push('k=' + this._keyId);
    Eif (this._ip) {
      parts.push('i=' + this._ip);
    }
    this._unsignedToken = parts.join(';');
    winston.debug('RoleToken created: ' + this._unsignedToken);
  }
 
  getPrincipal() {
    return this._principal;
  }
 
  getRoles() {
    return this._roles;
  }
 
  getProxyUser() {
    return this._proxyUser;
  }
 
  getDomainCompleteRoleSet() {
    return this._domainCompleteRoleSet;
  }
}
 
module.exports = RoleToken;