# All files / src/api keystore.coffee

14.95% Statements 16/107
0% Branches 0/5
0% Functions 0/13
14.95% Lines 16/107

    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
    2111x
    1x
    1x
    1x
    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

     Keystore = require('../model/keystore').Keystore
    Q = require 'q'
    logger = require 'winston'
    authorisation = require './authorisation'
    pem = require 'pem'

    utils = require "../utils"

    exports.getServerCert = ->
      # Must be admin
      if authorisation.inGroup('admin', this.authenticated) is false
        utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to getServerCert denied.", 'info'
        return

      try
        keystoreDoc = yield Keystore.findOne().select('cert').exec()
        this.body = keystoreDoc.cert
      catch err
        utils.logAndSetResponse this, 500, "Could not fetch the server cert via the API: #{err}", 'error'

    exports.getCACerts = ->
      # Must be admin
      if authorisation.inGroup('admin', this.authenticated) is false
        utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to getCACerts denied.", 'info'
        return

      try
        keystoreDoc = yield Keystore.findOne().select('ca').exec()
        this.body = keystoreDoc.ca
      catch err
        utils.logAndSetResponse this, 500, "Could not fetch the ca certs trusted by this server via the API: #{err}", 'error'

    exports.getCACert = (certId) ->
      # Must be admin
      if authorisation.inGroup('admin', this.authenticated) is false
        utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to getCACert by id denied.", 'info'
        return

      try
        keystoreDoc = yield Keystore.findOne().select('ca').exec()
        cert = keystoreDoc.ca.id(certId)

        this.body = cert
      catch err
        utils.logAndSetResponse this, 500, "Could not fetch ca cert by id via the API: #{err}", 'error'

    exports.setServerPassphrase = ->
    # Must be admin
      if authorisation.inGroup('admin', this.authenticated) is false
        utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to setServerPassphrase denied.", 'info'
        return

      try
        passphrase = this.request.body.passphrase
        keystoreDoc = yield Keystore.findOne().exec()
        keystoreDoc.passphrase = passphrase
        yield Q.ninvoke keystoreDoc, 'save'
        this.status = 201

      catch err
        utils.logAndSetResponse this, 500, "Could not set the passphrase  via the API: #{err}", 'error'

    exports.setServerCert = ->
      # Must be admin
      if authorisation.inGroup('admin', this.authenticated) is false
        utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to setServerCert by id denied.", 'info'
        return

      try
        cert = this.request.body.cert
        passphrase = this.request.body.passphrase
        readCertificateInfo = Q.denodeify pem.readCertificateInfo
        getFingerprint = Q.denodeify pem.getFingerprint
        try
          certInfo = yield readCertificateInfo cert
          fingerprint = yield getFingerprint cert
        catch err
          return utils.logAndSetResponse this, 400, "Could not add server cert via the API: #{err}", 'error'
        certInfo.data = cert
        certInfo.fingerprint = fingerprint.fingerprint

        keystoreDoc = yield Keystore.findOne().exec()
        keystoreDoc.cert = certInfo
        keystoreDoc.passphrase = passphrase

        yield Q.ninvoke keystoreDoc, 'save'
        this.status = 201
      catch err
        utils.logAndSetResponse this, 500, "Could not add server cert via the API: #{err}", 'error'

    exports.setServerKey = ->
      # Must be admin
      if authorisation.inGroup('admin', this.authenticated) is false
        utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to getServerKey by id denied.", 'info'
        return

      try
        key = this.request.body.key
        passphrase = this.request.body.passphrase
        keystoreDoc = yield Keystore.findOne().exec()
        keystoreDoc.key = key
        keystoreDoc.passphrase = passphrase
        yield Q.ninvoke keystoreDoc, 'save'
        this.status = 201
      catch err
        utils.logAndSetResponse this, 500, "Could not add server key via the API: #{err}", 'error'

    exports.addTrustedCert = ->
      # Must be admin
      if authorisation.inGroup('admin', this.authenticated) is false
        utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to addTrustedCert by id denied.", 'info'
        return

      try
        invalidCert = false
        chain = this.request.body.cert

        # Parse into an array in case this is a cert chain
        # (code derived from: http://www.benjiegillam.com/2012/06/node-dot-js-ssl-certificate-chain/)
        certs = []
        chain = chain.split "\n"
        cert = []
        for line in chain when line.length isnt 0
          cert.push line
          if line.match /-END CERTIFICATE-/
            certs.push ((cert.join "\n") + "\n")
            cert = []

        keystoreDoc = yield Keystore.findOne().exec()
        readCertificateInfo = Q.denodeify pem.readCertificateInfo
        getFingerprint = Q.denodeify pem.getFingerprint

        if certs.length < 1
          invalidCert = true

        for cert in certs
          try
            certInfo = yield readCertificateInfo cert
            fingerprint = yield getFingerprint cert
          catch err
            invalidCert = true
            continue
          certInfo.data = cert
          certInfo.fingerprint = fingerprint.fingerprint
          keystoreDoc.ca.push certInfo

        yield Q.ninvoke keystoreDoc, 'save'

        if invalidCert
          utils.logAndSetResponse this, 400, "Failed to add one more cert, are they valid? #{err}", 'error'
        else
          this.status = 201
      catch err
        utils.logAndSetResponse this, 500, "Could not add trusted cert via the API: #{err}", 'error'

    exports.removeCACert = (certId) ->
      # Must be admin
      if authorisation.inGroup('admin', this.authenticated) is false
        utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to removeCACert by id denied.", 'info'
        return

      try
        keystoreDoc = yield Keystore.findOne().exec()
        keystoreDoc.ca.id(certId).remove()
        yield Q.ninvoke keystoreDoc, 'save'
        this.status = 200
      catch err
        utils.logAndSetResponse this, 500, "Could not remove ca cert by id via the API: #{err}", 'error'

    exports.verifyServerKeys = ->
      # Must be admin
      if authorisation.inGroup('admin', this.authenticated) is false
        utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to verifyServerKeys.", 'info'
        return

      try
        try
          result = yield Q.nfcall getCertKeyStatus
        catch err
          return utils.logAndSetResponse this, 400, "Could not verify certificate and key, are they valid? #{err}", 'error'

        this.body =
          valid: result
        this.status = 200

      catch err
        utils.logAndSetResponse this, 500, "Could not determine validity via the API: #{err}", 'error'

    exports.getCertKeyStatus = getCertKeyStatus = (callback) ->

      Keystore.findOne (err, keystoreDoc) ->
        return callback err, null if err

        # if the key is encrypted but no passphrase is supplied, return  false instantly
        if /Proc-Type:.*ENCRYPTED/.test(keystoreDoc.key) and (not keystoreDoc.passphrase? or keystoreDoc.passphrase.length == 0)
          return callback null, false

        pem.getModulusFromProtected keystoreDoc.key, keystoreDoc.passphrase, (err, keyModulus) ->
          return callback err, null if err
          pem.getModulus keystoreDoc.cert.data, (err, certModulus) ->
            return callback err, null if err

            # if cert/key match and are valid
            if keyModulus.modulus is certModulus.modulus
              return callback null, true
            else
              return callback null, false