simple-gnupg-keyserver 1.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -1
- data/History.txt +0 -2
- data/Manifest.txt +4 -1
- data/README.rdoc +45 -40
- data/Rakefile +1 -0
- data/lib/simpleHKP/echo.rb +49 -8
- data/lib/simpleHKP/identities.rb +240 -0
- data/lib/simpleHKP/keys.rb +239 -0
- data/lib/simpleHKP/server.rb +349 -0
- data/lib/simpleHKP.rb +10 -489
- metadata +13 -9
    
        data/.gitignore
    CHANGED
    
    
    
        data/Manifest.txt
    CHANGED
    
    
    
        data/README.rdoc
    CHANGED
    
    | @@ -1,32 +1,22 @@ | |
| 1 1 | 
             
            = rGem-simple-gnupg-keyserver
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 3 | 
            +
            code :: https://github.com/stephengaito/rGem-simple-gnupg-keyserver
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
            SimpleHKP is a simple Ruby/rack based GnuPG HKP key server.
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            Its sole task is to supply a limited number of GnuPG _public_ keys 
         | 
| 10 | 
            -
            to/from MonkeySphere on a limited number of machines/servers. It is 
         | 
| 11 | 
            -
            _not_ meant to server hundreds of keys, so it is not meant as a truely 
         | 
| 12 | 
            -
            public key server.
         | 
| 13 | 
            -
             | 
| 14 | 
            -
            If you need to run your own _large_ scale production Key Sever then use 
         | 
| 15 | 
            -
            {SKS}[https://bitbucket.org/skskeyserver/sks-keyserver/wiki/Home]. You 
         | 
| 16 | 
            -
            might want to read Paul Bauer's {How To Setup A Free PGP Key Server in 
         | 
| 17 | 
            -
            Ubuntu}[http://www.bauer-power.net/2010/05/how-to-setup-free-pgp-key-server-in.html].
         | 
| 5 | 
            +
            docs :: https://stephengaito.github.io/rGem-simple-gnupg-keyserver
         | 
| 18 6 |  | 
| 19 | 
            -
             | 
| 7 | 
            +
            == DESCRIPTION:
         | 
| 20 8 |  | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 9 | 
            +
            SimpleHKP is a simple Ruby/rack based GnuPG extendedHKP key and 
         | 
| 10 | 
            +
            identity server packaged as a standard Ruby Gem. 
         | 
| 23 11 |  | 
| 24 | 
            -
             | 
| 12 | 
            +
            As such it conforms to an {extended 
         | 
| 13 | 
            +
            version}[http://stephengaito.github.io/rGem-simple-gnupg-keyserver/] of 
         | 
| 14 | 
            +
            {the OpenPGP HTTP Keyserver Protocol (HKP) 
         | 
| 15 | 
            +
            (draft-shaw-openpgp-hkp-00.txt)}[http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00].
         | 
| 25 16 |  | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
            which are matched against each key's key data.
         | 
| 17 | 
            +
            The {associated 
         | 
| 18 | 
            +
            documentation}[https://stephengaito.github.io/rGem-simple-gnupg-keyserver] 
         | 
| 19 | 
            +
            provides more detail.
         | 
| 30 20 |  | 
| 31 21 | 
             
            === Security:
         | 
| 32 22 |  | 
| @@ -47,6 +37,13 @@ This rack application is intentionally as self contained and simple as | |
| 47 37 | 
             
            possible.  If you have any security concerns, this rack application is 
         | 
| 48 38 | 
             
            easily readable.
         | 
| 49 39 |  | 
| 40 | 
            +
            When used with SimpleMonkey, this SimpleHKP acts like a distributed 
         | 
| 41 | 
            +
            password file. All important (non-public identity) details are 
         | 
| 42 | 
            +
            encrypted, however, the collection of servers, users and their 
         | 
| 43 | 
            +
            associated roles are visibly in the "public domain". It is important to 
         | 
| 44 | 
            +
            run your own collection of SimpleHKP servers *inside* a controlled 
         | 
| 45 | 
            +
            domain or VPN.
         | 
| 46 | 
            +
             | 
| 50 47 | 
             
            == SYNOPSIS:
         | 
| 51 48 |  | 
| 52 49 | 
             
            A typical rackup.ru file might be:
         | 
| @@ -68,13 +65,14 @@ key/value pairs. | |
| 68 65 | 
             
            The current default options are:
         | 
| 69 66 |  | 
| 70 67 | 
             
              defaultOptions = {
         | 
| 71 | 
            -
                'debug'        => false, | 
| 72 | 
            -
                'title'        => 'SimpleHKP' | 
| 73 | 
            -
                'simpleHKPdir' => 'simpleHKP', | 
| 74 | 
            -
                'keyDir'       => 'keys', | 
| 75 | 
            -
                ' | 
| 76 | 
            -
                ' | 
| 77 | 
            -
                ' | 
| 68 | 
            +
                'debug'        => false,        # should debug output to logged?
         | 
| 69 | 
            +
                'title'        => 'SimpleHKP'   # the title used by the default headerHTML
         | 
| 70 | 
            +
                'simpleHKPdir' => 'simpleHKP',  # base disk path to simpleHKP disk space
         | 
| 71 | 
            +
                'keyDir'       => 'keys',       # subdir to key storage directory
         | 
| 72 | 
            +
                'idDir'        => 'identities', # subdir to identity storage directory
         | 
| 73 | 
            +
                'mediaDir'     => 'media',      # subdir to any css, js, images etc
         | 
| 74 | 
            +
                'htmlDir'      => 'html',       # subdir to html partials
         | 
| 75 | 
            +
                'mimeMap'      => {             # a file ext to mime mapping
         | 
| 78 76 | 
             
                  'css'  => 'text/css',
         | 
| 79 77 | 
             
                  'html' => 'text/html',
         | 
| 80 78 | 
             
                  'js'   => 'text/javascript'
         | 
| @@ -86,14 +84,16 @@ the use of humans: | |
| 86 84 |  | 
| 87 85 | 
             
            * header.html
         | 
| 88 86 | 
             
            * defaultBody.html
         | 
| 89 | 
            -
            *  | 
| 90 | 
            -
            *  | 
| 87 | 
            +
            * lookupKeysForm.html
         | 
| 88 | 
            +
            * lookupIdentitiesForm.html
         | 
| 89 | 
            +
            * uploadKeyForm.html
         | 
| 90 | 
            +
            * uploadIdentityForm.html
         | 
| 91 91 | 
             
            * footer.html
         | 
| 92 92 |  | 
| 93 93 | 
             
            == SYNCHRONIZATION
         | 
| 94 94 |  | 
| 95 | 
            -
            A 'simpleHKP/echo' class has been added which knows how to echo  | 
| 96 | 
            -
            one key server to another.
         | 
| 95 | 
            +
            A 'simpleHKP/echo' class has been added which knows how to echo 
         | 
| 96 | 
            +
            keys/identities from one key server to another.
         | 
| 97 97 |  | 
| 98 98 | 
             
            So that for example the following ruby script could be placed into one 
         | 
| 99 99 | 
             
            of your machine's /etc/daily directories and the script would ensure 
         | 
| @@ -101,13 +101,13 @@ all key servers are synchronized daily. | |
| 101 101 |  | 
| 102 102 | 
             
              #!/usr/bin/env ruby
         | 
| 103 103 |  | 
| 104 | 
            -
              # A simple key server sychronization example
         | 
| 104 | 
            +
              # A simple key/identity server sychronization example
         | 
| 105 105 | 
             
              #
         | 
| 106 106 | 
             
              # We use a hub-spoke model. We choose one "hub" key server to act as
         | 
| 107 | 
            -
              # the master key server and amalgamate all of the keys from | 
| 108 | 
            -
              # key server back to the hub key server. We then push the | 
| 109 | 
            -
              # keys in the hub back to the spokes, so that | 
| 110 | 
            -
              # servers have the same keys.
         | 
| 107 | 
            +
              # the master key server and amalgamate all of the keys/identities from
         | 
| 108 | 
            +
              # the spoke key server back to the hub key server. We then push the
         | 
| 109 | 
            +
              # amalgamated keys/identities in the hub back to the spokes, so that
         | 
| 110 | 
            +
              # after two passes, all key servers have the same keys/identities.
         | 
| 111 111 |  | 
| 112 112 | 
             
              require 'simpleHKP/echo'
         | 
| 113 113 |  | 
| @@ -118,13 +118,13 @@ all key servers are synchronized daily. | |
| 118 118 | 
             
                'spokeKeyServer3'
         | 
| 119 119 | 
             
              ]
         | 
| 120 120 |  | 
| 121 | 
            -
              # Start by bringing all spoke keys into the hub
         | 
| 121 | 
            +
              # Start by bringing all spoke keys/identities into the hub
         | 
| 122 122 | 
             
              #
         | 
| 123 123 | 
             
              spokeKeyServers.each do | aKeyServer |
         | 
| 124 124 | 
             
                SimpleHKPEcho.echoFromTo(aKeyServer, hubKeyServer)
         | 
| 125 125 | 
             
              end
         | 
| 126 126 |  | 
| 127 | 
            -
              # now send the amalgamated hub keys back to each spoke
         | 
| 127 | 
            +
              # now send the amalgamated hub keys/identities back to each spoke
         | 
| 128 128 | 
             
              #
         | 
| 129 129 | 
             
              spokeKeyServers.each do | aKeyServer |
         | 
| 130 130 | 
             
                SimpleHKPEcho.echoFromTo(hubKeyServer, aKeyServer)
         | 
| @@ -137,6 +137,10 @@ The SimpleHKPEcho.echoFromTo method fails gracefully if a given key | |
| 137 137 | 
             
            server is offline, by simply returning. In the example above, all other 
         | 
| 138 138 | 
             
            echo pairs (from, to) will be tried.
         | 
| 139 139 |  | 
| 140 | 
            +
            To just echo either the keys or identities you can use 
         | 
| 141 | 
            +
            SimpleHKPEcho.echoKeysFromTo or SimpleHKPEcho.echoIdentitiesFromTo 
         | 
| 142 | 
            +
            respectively.
         | 
| 143 | 
            +
             | 
| 140 144 | 
             
            == REQUIREMENTS:
         | 
| 141 145 |  | 
| 142 146 | 
             
            There are explicitly no external Ruby requirements other than Ruby and 
         | 
| @@ -175,3 +179,4 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
| 175 179 | 
             
            TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
         | 
| 176 180 | 
             
            SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
| 177 181 |  | 
| 182 | 
            +
             | 
    
        data/Rakefile
    CHANGED
    
    
    
        data/lib/simpleHKP/echo.rb
    CHANGED
    
    | @@ -2,10 +2,11 @@ require 'uri' | |
| 2 2 | 
             
            require 'net/http'
         | 
| 3 3 | 
             
            require 'pp'
         | 
| 4 4 |  | 
| 5 | 
            -
            # This code provides a simple way to ensure keys are  | 
| 6 | 
            -
            # between a pair of key servers.
         | 
| 5 | 
            +
            # This code provides a simple way to ensure keys/identities are 
         | 
| 6 | 
            +
            # synchronized between a pair of key/identity servers.
         | 
| 7 7 |  | 
| 8 | 
            -
            # It conforms to | 
| 8 | 
            +
            # It conforms to our simple extension to: 
         | 
| 9 | 
            +
            #   The OpenPGP HTTP Keyserver Protocol (HKP)
         | 
| 9 10 | 
             
            #   draft-shaw-openpgp-hkp-00.txt
         | 
| 10 11 | 
             
            #     http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00
         | 
| 11 12 |  | 
| @@ -48,7 +49,7 @@ class SimpleHKPEcho | |
| 48 49 | 
             
                  aKeyServerStr
         | 
| 49 50 | 
             
                end
         | 
| 50 51 |  | 
| 51 | 
            -
                def  | 
| 52 | 
            +
                def echoKeysFromTo(fromKeyServer, toKeyServer, options = {})
         | 
| 52 53 | 
             
                  begin
         | 
| 53 54 | 
             
                    debug = options.delete('debug')
         | 
| 54 55 | 
             
                    puts fromKeyServer if debug
         | 
| @@ -59,7 +60,7 @@ class SimpleHKPEcho | |
| 59 60 | 
             
                    puts toKeyServer if debug
         | 
| 60 61 |  | 
| 61 62 | 
             
                    keys = Array.new
         | 
| 62 | 
            -
                    url = URI.parse(fromKeyServer+'/lookup?search=&op=index&options=mr')
         | 
| 63 | 
            +
                    url = URI.parse(fromKeyServer+'/pks/lookup?search=&op=index&options=mr')
         | 
| 63 64 | 
             
                    response = Net::HTTP.get_response(url)
         | 
| 64 65 | 
             
                    response.body.each_line do | aLine |
         | 
| 65 66 | 
             
                      next unless aLine =~ /^pub/
         | 
| @@ -68,19 +69,59 @@ class SimpleHKPEcho | |
| 68 69 | 
             
                    pp keys if debug
         | 
| 69 70 | 
             
                    keys.each do | aKey |
         | 
| 70 71 | 
             
                      keyData = ""
         | 
| 71 | 
            -
                      url = URI.parse(fromKeyServer+"/lookup?op=get&options=mr&search=#{aKey}")
         | 
| 72 | 
            +
                      url = URI.parse(fromKeyServer+"/pks/lookup?op=get&options=mr&search=#{aKey}")
         | 
| 72 73 | 
             
                      response = Net::HTTP.get_response(url)
         | 
| 73 74 | 
             
                      keyData = response.body
         | 
| 74 75 | 
             
                      puts aKey if debug
         | 
| 75 76 | 
             
                      puts keyData if debug
         | 
| 76 | 
            -
                      url = URI.parse(toKeyServer+'/add')
         | 
| 77 | 
            +
                      url = URI.parse(toKeyServer+'/pks/add')
         | 
| 77 78 | 
             
                      Net::HTTP.post_form(url, { 'keytext' => keyData })
         | 
| 78 79 | 
             
                    end
         | 
| 79 | 
            -
                  rescue  | 
| 80 | 
            +
                  rescue Exception => ex
         | 
| 80 81 | 
             
                    puts "Cound not echo keys from #{fromKeyServer} to #{toKeyServer}"
         | 
| 82 | 
            +
                    puts ex
         | 
| 81 83 | 
             
                  end
         | 
| 82 84 | 
             
                end
         | 
| 83 85 |  | 
| 86 | 
            +
                def echoIdentitiesFromTo(fromKeyServer, toKeyServer, options = {})
         | 
| 87 | 
            +
                  begin
         | 
| 88 | 
            +
                    debug = options.delete('debug')
         | 
| 89 | 
            +
                    puts fromKeyServer if debug
         | 
| 90 | 
            +
                    fromKeyServer = convertToHttp(fromKeyServer)
         | 
| 91 | 
            +
                    puts fromKeyServer if debug
         | 
| 92 | 
            +
                    puts toKeyServer if debug
         | 
| 93 | 
            +
                    toKeyServer   = convertToHttp(toKeyServer)
         | 
| 94 | 
            +
                    puts toKeyServer if debug
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                    identities = Array.new
         | 
| 97 | 
            +
                    url = URI.parse(fromKeyServer+'/pis/lookup?search=&op=index&options=mr')
         | 
| 98 | 
            +
                    response = Net::HTTP.get_response(url)
         | 
| 99 | 
            +
                    response.body.each_line do | aLine |
         | 
| 100 | 
            +
                      next unless aLine =~ /^idn/
         | 
| 101 | 
            +
                      identities.push(aLine.split(/:/)[1])
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                    pp identities if debug
         | 
| 104 | 
            +
                    identities.each do | anIdFile |
         | 
| 105 | 
            +
                      idData = ""
         | 
| 106 | 
            +
                      url = URI.parse(fromKeyServer+"/pis/lookup?op=get&options=mr&search=#{anIdFile}")
         | 
| 107 | 
            +
                      response = Net::HTTP.get_response(url)
         | 
| 108 | 
            +
                      idData = response.body
         | 
| 109 | 
            +
                      puts anIdFile if debug
         | 
| 110 | 
            +
                      puts idData if debug
         | 
| 111 | 
            +
                      url = URI.parse(toKeyServer+'/pis/add')
         | 
| 112 | 
            +
                      Net::HTTP.post_form(url, { 'keytext' => idData })
         | 
| 113 | 
            +
                    end
         | 
| 114 | 
            +
                  rescue Exception => ex
         | 
| 115 | 
            +
                    puts "Cound not echo identities from #{fromKeyServer} to #{toKeyServer}"
         | 
| 116 | 
            +
                    puts ex
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                def echoFromTo(fromKeyServer, toKeyServer, options = {})
         | 
| 121 | 
            +
                  echoKeysFromTo(fromKeyServer, toKeyServer, options)
         | 
| 122 | 
            +
                  echoIdentitiesFromTo(fromKeyServer, toKeyServer, options)
         | 
| 123 | 
            +
                end
         | 
| 124 | 
            +
             | 
| 84 125 | 
             
              end
         | 
| 85 126 |  | 
| 86 127 | 
             
            end
         | 
| @@ -0,0 +1,240 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            module SimpleHKP
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              module Identities
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                def loadIdentityFile(fileName)
         | 
| 7 | 
            +
                  identityContents = ''
         | 
| 8 | 
            +
                  begin
         | 
| 9 | 
            +
                    identityContents = File.open(fileName, 'r').read
         | 
| 10 | 
            +
                  rescue Exception => e
         | 
| 11 | 
            +
                    puts "Error reading file #{fileName}: #{e.message}"
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                  identityContents
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def getIdentityMetaData(fileContents)
         | 
| 17 | 
            +
                  metaData = Hash.new
         | 
| 18 | 
            +
                  begin
         | 
| 19 | 
            +
                    if fileContents =~ /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
         | 
| 20 | 
            +
                      metaData = SafeYAML.load($1)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  rescue SyntaxError => e
         | 
| 23 | 
            +
                    puts "YAML Exception reading #{fileName}: #{e.message}"
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  if !metaData.has_key?('role') ||
         | 
| 27 | 
            +
                     !metaData.has_key?('userKeyID') ||
         | 
| 28 | 
            +
                     metaData['userKeyID'] !~ /^\h+$/ then
         | 
| 29 | 
            +
                    puts "WARNING the identity file has NO role or userKeyID or a malformed userKeyID"
         | 
| 30 | 
            +
                    return ''
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  #
         | 
| 34 | 
            +
                  # normalize the meta data with any missing (but useful) values
         | 
| 35 | 
            +
                  #
         | 
| 36 | 
            +
                  metaData['userID'] = metaData['userKeyID'] unless
         | 
| 37 | 
            +
                    metaData.has_key?('userID')
         | 
| 38 | 
            +
                  metaData['algorithm'] = 'unknown' unless metaData.has_key?('algorithm')
         | 
| 39 | 
            +
                  metaData['created'] = Date.today.strftime("%Y%m%d") unless
         | 
| 40 | 
            +
                    metaData.has_key?('created')
         | 
| 41 | 
            +
                  metaData['expires'] = Date.today.strftime("%Y%m%d") unless
         | 
| 42 | 
            +
                    metaData.has_key?('expires')
         | 
| 43 | 
            +
                  metaData['flags'] = '' unless metaData.has_key?('flags')
         | 
| 44 | 
            +
                  #
         | 
| 45 | 
            +
                  # compute the idFile for searching
         | 
| 46 | 
            +
                  #
         | 
| 47 | 
            +
                  metaData['identity'] = metaData['role']+'-'+metaData['userKeyID']
         | 
| 48 | 
            +
                  #
         | 
| 49 | 
            +
                  # wrap the whole meta data into a searchable string
         | 
| 50 | 
            +
                  #
         | 
| 51 | 
            +
                  metaData['searchData'] = metaData.to_s
         | 
| 52 | 
            +
                  metaData
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def indexIdentityFiles
         | 
| 56 | 
            +
                  puts "SimpleHKP: indexing identities" if @debug
         | 
| 57 | 
            +
                  @idMetaData = Hash.new
         | 
| 58 | 
            +
                  Dir.glob(@idDir+'/**/*.asc') do | aFile |
         | 
| 59 | 
            +
                    idFile = File.basename(aFile, '.*')
         | 
| 60 | 
            +
                    puts aFile if @debug
         | 
| 61 | 
            +
                    puts idFile if @debug
         | 
| 62 | 
            +
                    identityFile = loadIdentityFile(aFile)
         | 
| 63 | 
            +
                    metaData = getIdentityMetaData(identityFile)
         | 
| 64 | 
            +
                    if metaData.empty? then
         | 
| 65 | 
            +
                      puts "WARNING the identity file [#{aFile}] is malformed!"
         | 
| 66 | 
            +
                    else
         | 
| 67 | 
            +
                      @idMetaData[idFile] = metaData
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                    puts @idMetaData[idFile] if @debug
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
            #      File.open(@idIndex, 'w') do | indexFile |
         | 
| 72 | 
            +
            #        indexFile.puts "# identity metaData index created: #{DateTime.now}"
         | 
| 73 | 
            +
            #        indexFile.write(YAML.dump(@idMetaData))
         | 
| 74 | 
            +
            #      end
         | 
| 75 | 
            +
                  puts "SimpleHKP: finished indexing identities" if @debug
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                def loadIdentities
         | 
| 79 | 
            +
                  @idLookupData = Hash.new
         | 
| 80 | 
            +
                  puts "SimpleHKP: loading identities" if @debug
         | 
| 81 | 
            +
                  indexIdentityFiles
         | 
| 82 | 
            +
                  pp @idMetaData
         | 
| 83 | 
            +
                  puts "SimpleHKP: finished loading identities" if @debug
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                def storeIdentity(env)
         | 
| 87 | 
            +
                  #
         | 
| 88 | 
            +
                  # decode the post data
         | 
| 89 | 
            +
                  #
         | 
| 90 | 
            +
                  keys = URI.decode_www_form(env['rack.input'].read)
         | 
| 91 | 
            +
                  #
         | 
| 92 | 
            +
                  # ensure we are being sent a key
         | 
| 93 | 
            +
                  #
         | 
| 94 | 
            +
                  return replyBadRequest("No keytext field in post data") unless
         | 
| 95 | 
            +
                    keys[0][0] =~ /keytext/
         | 
| 96 | 
            +
                  pp keys[0][1] if @debug
         | 
| 97 | 
            +
                  metaData = getIdentityMetaData(keys[0][1])
         | 
| 98 | 
            +
                  pp metaData
         | 
| 99 | 
            +
                  return replyBadRequest("malformed identity metaData") if metaData.empty?
         | 
| 100 | 
            +
                  #
         | 
| 101 | 
            +
                  # store this key as a flat file with the name of the keyID.asc
         | 
| 102 | 
            +
                  #
         | 
| 103 | 
            +
                  idFile = metaData['role']+'-'+metaData['userKeyID']
         | 
| 104 | 
            +
                  idFileName = @idDir+'/'+idFile+'.asc'
         | 
| 105 | 
            +
                  idChanged = 'unchanged'
         | 
| 106 | 
            +
                  if !File.exists?(idFileName) || keys[0][1] != File.read(idFileName) then
         | 
| 107 | 
            +
                    idChanged = 'changed'
         | 
| 108 | 
            +
                    File.write(idFileName, keys[0][1])
         | 
| 109 | 
            +
                    @idMetaData[idFile] = metaData
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
                  pp @idMetaData
         | 
| 112 | 
            +
                  #
         | 
| 113 | 
            +
                  # return OK
         | 
| 114 | 
            +
                  #
         | 
| 115 | 
            +
                  @statusCode = 200
         | 
| 116 | 
            +
                  @body << @headerHtml
         | 
| 117 | 
            +
                  @body << "<h1 class=\"simpleHKP-storedIdentity\">Stored identity: [#{idFile}]</h1>"
         | 
| 118 | 
            +
                  @body << "<p>identity #{idChanged}</p>"
         | 
| 119 | 
            +
                  @body << "<h2 class=\"simpleHKP-identityDataID\">Identity data for identity: [#{idFile}]</h2>"
         | 
| 120 | 
            +
                  @body << '<pre class="simpleHKP-identityData">'+keys[0][1]+"</pre>\n"
         | 
| 121 | 
            +
                  @body << @footer
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                def lookUpIdentity(queryString)
         | 
| 125 | 
            +
                  return replyBadRequest("No search field in queryString") unless
         | 
| 126 | 
            +
                    queryString.has_key?('search')
         | 
| 127 | 
            +
                  #
         | 
| 128 | 
            +
                  # normalize the search string
         | 
| 129 | 
            +
                  #
         | 
| 130 | 
            +
                  searchString = queryString['search']
         | 
| 131 | 
            +
                  searchRegexp = Regexp.new(searchString,
         | 
| 132 | 
            +
                                            Regexp::IGNORECASE | Regexp::MULTILINE)
         | 
| 133 | 
            +
                  puts searchRegexp if @debug
         | 
| 134 | 
            +
                  #
         | 
| 135 | 
            +
                  # (linearly) look through the hash of known identities
         | 
| 136 | 
            +
                  # looking for the FIRST match
         | 
| 137 | 
            +
                  #
         | 
| 138 | 
            +
                  idFile = nil
         | 
| 139 | 
            +
                  pp @idMetaData
         | 
| 140 | 
            +
                  @idMetaData.each_pair do | idKey, idData |
         | 
| 141 | 
            +
                    next unless idData['searchData'] =~ searchRegexp
         | 
| 142 | 
            +
                    puts "FOUND #{idKey} (#{idData})" if @debug
         | 
| 143 | 
            +
                    idFile = idKey
         | 
| 144 | 
            +
                    break
         | 
| 145 | 
            +
                  end
         | 
| 146 | 
            +
                  return replyNotFound if idFile.nil?
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  idFileName = @idDir+'/'+idFile+'.asc'
         | 
| 149 | 
            +
                  if queryString.has_key?('options') &&
         | 
| 150 | 
            +
                    queryString['options'] == 'mr' then
         | 
| 151 | 
            +
                    #
         | 
| 152 | 
            +
                    # return the key data in machine readable format
         | 
| 153 | 
            +
                    #
         | 
| 154 | 
            +
                    @header = {
         | 
| 155 | 
            +
                      'Content-Type' => 'text/plain; charset=utf-8',
         | 
| 156 | 
            +
                      'Content-Disposition' =>
         | 
| 157 | 
            +
                        'attachment; filename=' + File.basename(idFileName)
         | 
| 158 | 
            +
                    }
         | 
| 159 | 
            +
                    puts @header if @debug
         | 
| 160 | 
            +
                    @body << File.read(idFileName)
         | 
| 161 | 
            +
                  else
         | 
| 162 | 
            +
                    #
         | 
| 163 | 
            +
                    # return the key data for a human to read
         | 
| 164 | 
            +
                    #
         | 
| 165 | 
            +
                    @body << @headerHtml
         | 
| 166 | 
            +
                    @body << @lookupIdentitiesForm
         | 
| 167 | 
            +
                    @body << '<h1 class="simpleHKP-identityAscIdFile">Identity: '+idFile+'</h1>'
         | 
| 168 | 
            +
                    @body << '<h2 class="simpleHKP-identityAscTitle">Identity contents:</h2>'
         | 
| 169 | 
            +
                    @body << '<pre class="simpleHKP-identityAsc">'
         | 
| 170 | 
            +
                    @body << File.read(idFileName)
         | 
| 171 | 
            +
                    @body << '</pre>'
         | 
| 172 | 
            +
                    @body << @footer
         | 
| 173 | 
            +
                  end
         | 
| 174 | 
            +
                end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                def indexIdentities(queryString)
         | 
| 177 | 
            +
                  return replyBadRequest("No search field in queryString") unless
         | 
| 178 | 
            +
                    queryString.has_key?('search')
         | 
| 179 | 
            +
                  #
         | 
| 180 | 
            +
                  # normalize the search string
         | 
| 181 | 
            +
                  #
         | 
| 182 | 
            +
                  searchString = queryString['search']
         | 
| 183 | 
            +
                  searchRegexp = Regexp.new(searchString,
         | 
| 184 | 
            +
                                            Regexp::IGNORECASE | Regexp::MULTILINE)
         | 
| 185 | 
            +
                  puts searchRegexp if @debug
         | 
| 186 | 
            +
                  #
         | 
| 187 | 
            +
                  # accumulate the idFiles of any identities that match the search
         | 
| 188 | 
            +
                  #
         | 
| 189 | 
            +
                  idFiles = Array.new
         | 
| 190 | 
            +
                  pp @idMetaData
         | 
| 191 | 
            +
                  @idMetaData.each_pair do | idKey, idData |
         | 
| 192 | 
            +
                    next unless idData['searchData'] =~ searchRegexp
         | 
| 193 | 
            +
                    puts "FOUND #{idKey} (#{idData})" if @debug
         | 
| 194 | 
            +
                    idFiles.push(idKey)
         | 
| 195 | 
            +
                  end
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                  if queryString.has_key?('options') &&
         | 
| 198 | 
            +
                    queryString['options'] == 'mr' then
         | 
| 199 | 
            +
                    #
         | 
| 200 | 
            +
                    # return a machine readable list of the dFiles found
         | 
| 201 | 
            +
                    #
         | 
| 202 | 
            +
                    @header = { 'Content-Type' => 'text/plain' }
         | 
| 203 | 
            +
                    @body << "info:1:#{idFiles.size}\n"
         | 
| 204 | 
            +
                    idFiles.each do | anIdFile |
         | 
| 205 | 
            +
                      next unless @idMetaData.has_key?(anIdFile)
         | 
| 206 | 
            +
                      idData   = @idMetaData[anIdFile]
         | 
| 207 | 
            +
                      idnData = [ "idn" ]
         | 
| 208 | 
            +
                      idnData << anIdFile
         | 
| 209 | 
            +
                      idnData << idData['role']
         | 
| 210 | 
            +
                      idnData << idData['userID'].gsub(/:/, '\x3a')
         | 
| 211 | 
            +
                      idnData << idData['algorithm']
         | 
| 212 | 
            +
                      idnData << idData['created']
         | 
| 213 | 
            +
                      idnData << idData['expires']
         | 
| 214 | 
            +
                      idnData << idData['flags']
         | 
| 215 | 
            +
                      @body << idnData.join(':')
         | 
| 216 | 
            +
                      @body << "\n"
         | 
| 217 | 
            +
                    end
         | 
| 218 | 
            +
                  else
         | 
| 219 | 
            +
                    #
         | 
| 220 | 
            +
                    # return a (simple) human readable list of the keys found
         | 
| 221 | 
            +
                    #
         | 
| 222 | 
            +
                    @body << @headerHtml
         | 
| 223 | 
            +
                    @body << @lookupIdentitiesForm
         | 
| 224 | 
            +
                    @body << '<h1 class="simpleHKP-searchString">Identities matching: ['+searchString+']</h1><ul class="simpleHKP-searchList">'
         | 
| 225 | 
            +
                    idFiles.each do | anIdFile |
         | 
| 226 | 
            +
                      next unless @idMetaData.has_key?(anIdFile)
         | 
| 227 | 
            +
                      idData   = @idMetaData[anIdFile]
         | 
| 228 | 
            +
                      idStr = "  <li class=\"simpleHKP-searchItem\"><a href=\"/pis/lookup?op=get&search=#{anIdFile}\">"
         | 
| 229 | 
            +
                      idStr << anIdFile+': '+idData['userID']+' ('+idData['role']+')'
         | 
| 230 | 
            +
                      idStr <<  "</a></li>\n"
         | 
| 231 | 
            +
                      @body << idStr
         | 
| 232 | 
            +
                    end
         | 
| 233 | 
            +
                    @body << '</ul>'
         | 
| 234 | 
            +
                    @body << @footer
         | 
| 235 | 
            +
                  end
         | 
| 236 | 
            +
                  pp @body if @debug
         | 
| 237 | 
            +
                end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
              end
         | 
| 240 | 
            +
            end
         |