Statistics
| Branch: | Tag: | Revision:

root / lib / serializer.py @ 541822e0

History | View | Annotate | Download (4.5 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2008 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21
"""Serializer abstraction module
22

23
This module introduces a simple abstraction over the serialization
24
backend (currently json).
25

26
"""
27

    
28
import simplejson
29
import re
30
import hmac
31

    
32
from ganeti import errors
33

    
34
try:
35
  from hashlib import sha1
36
except ImportError:
37
  import sha as sha1
38

    
39
# Check whether the simplejson module supports indentation
40
_JSON_INDENT = 2
41
try:
42
  simplejson.dumps(1, indent=_JSON_INDENT)
43
except TypeError:
44
  _JSON_INDENT = None
45

    
46
_RE_EOLSP = re.compile('[ \t]+$', re.MULTILINE)
47

    
48

    
49
def DumpJson(data, indent=True):
50
  """Serialize a given object.
51

52
  @param data: the data to serialize
53
  @param indent: whether to indent output (depends on simplejson version)
54

55
  @return: the string representation of data
56

57
  """
58
  if not indent or _JSON_INDENT is None:
59
    txt = simplejson.dumps(data)
60
  else:
61
    txt = simplejson.dumps(data, indent=_JSON_INDENT)
62

    
63
  txt = _RE_EOLSP.sub("", txt)
64
  if not txt.endswith('\n'):
65
    txt += '\n'
66
  return txt
67

    
68

    
69
def LoadJson(txt):
70
  """Unserialize data from a string.
71

72
  @param txt: the json-encoded form
73

74
  @return: the original data
75

76
  """
77
  return simplejson.loads(txt)
78

    
79

    
80
def DumpSignedJson(data, key, salt=None):
81
  """Serialize a given object and authenticate it.
82

83
  @param data: the data to serialize
84
  @param key: shared hmac key
85
  @return: the string representation of data signed by the hmac key
86

87
  """
88
  txt = DumpJson(data, indent=False)
89
  if salt is None:
90
    salt = ''
91
  signed_dict = {
92
    'msg': txt,
93
    'salt': salt,
94
    'hmac': hmac.new(key, salt + txt, sha1).hexdigest(),
95
  }
96
  return DumpJson(signed_dict)
97

    
98

    
99
def LoadSignedJson(txt, key, salt_verifier=None):
100
  """Verify that a given message was signed with the given key, and load it.
101

102
  @param txt: json-encoded hmac-signed message
103
  @param key: shared hmac key
104
  @param salt_verifier: function taking a salt as input and returning boolean
105
  @rtype: tuple of original data, string
106
  @return: (original data, salt)
107
  @raises errors.SignatureError: if the message signature doesn't verify
108

109
  """
110
  signed_dict = LoadJson(txt)
111
  if not isinstance(signed_dict, dict):
112
    raise errors.SignatureError('Invalid external message')
113
  try:
114
    msg = signed_dict['msg']
115
    salt = signed_dict['salt']
116
    hmac_sign = signed_dict['hmac']
117
  except KeyError:
118
    raise errors.SignatureError('Invalid external message')
119

    
120
  if salt and not salt_verifier:
121
    raise errors.SignatureError('Salted message is not verified')
122
  elif salt_verifier is not None:
123
    if not salt_verifier(salt):
124
      raise errors.SignatureError('Invalid salt')
125

    
126
  if hmac.new(key, salt + msg, sha1).hexdigest() != hmac_sign:
127
    raise errors.SignatureError('Invalid Signature')
128
  return LoadJson(msg)
129

    
130

    
131
def SaltEqualTo(expected):
132
  """Helper salt verifier function that checks for equality.
133

134
  @type expected: string
135
  @param expected: expected salt
136
  @rtype: function
137
  @return: salt verifier that returns True if the target salt is "x"
138

139
  """
140
  return lambda salt: salt == expected
141

    
142

    
143
def SaltIn(expected):
144
  """Helper salt verifier function that checks for equality.
145

146
  @type expected: collection
147
  @param expected: collection of possible valid salts
148
  @rtype: function
149
  @return: salt verifier that returns True if the salt is in the collection
150

151
  """
152
  return lambda salt: salt in expected
153

    
154

    
155
def SaltInRange(min, max):
156
  """Helper salt verifier function that checks for equality.
157

158
  @type min: integer
159
  @param min: minimum salt value
160
  @type max: integer
161
  @param max: maximum salt value
162
  @rtype: function
163
  @return: salt verifier that returns True if the salt is in the min,max range
164

165
  """
166
  def _CheckSaltInRange(salt):
167
    try:
168
      i_salt = int(salt)
169
    except (TypeError, ValueError), err:
170
      return False
171

    
172
    return i_salt > min and i_salt < max
173

    
174
  return _CheckSaltInRange
175

    
176

    
177
Dump = DumpJson
178
Load = LoadJson
179
DumpSigned = DumpSignedJson
180
LoadSigned = LoadSignedJson