Statistics
| Branch: | Tag: | Revision:

root / lib / serializer.py @ 0a71aa17

History | View | Annotate | Download (3.6 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

    
40
_JSON_INDENT = 2
41

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

    
44

    
45
def _GetJsonDumpers(_encoder_class=simplejson.JSONEncoder):
46
  """Returns two JSON functions to serialize data.
47

48
  @rtype: (callable, callable)
49
  @return: The function to generate a compact form of JSON and another one to
50
           generate a more readable, indented form of JSON (if supported)
51

52
  """
53
  plain_encoder = _encoder_class(sort_keys=True)
54

    
55
  # Check whether the simplejson module supports indentation
56
  try:
57
    indent_encoder = _encoder_class(indent=_JSON_INDENT, sort_keys=True)
58
  except TypeError:
59
    # Indentation not supported
60
    indent_encoder = plain_encoder
61

    
62
  return (plain_encoder.encode, indent_encoder.encode)
63

    
64

    
65
(_DumpJson, _DumpJsonIndent) = _GetJsonDumpers()
66

    
67

    
68
def DumpJson(data, indent=True):
69
  """Serialize a given object.
70

71
  @param data: the data to serialize
72
  @param indent: whether to indent output (depends on simplejson version)
73

74
  @return: the string representation of data
75

76
  """
77
  if indent:
78
    fn = _DumpJsonIndent
79
  else:
80
    fn = _DumpJson
81

    
82
  txt = _RE_EOLSP.sub("", fn(data))
83
  if not txt.endswith('\n'):
84
    txt += '\n'
85

    
86
  return txt
87

    
88

    
89
def LoadJson(txt):
90
  """Unserialize data from a string.
91

92
  @param txt: the json-encoded form
93

94
  @return: the original data
95

96
  """
97
  return simplejson.loads(txt)
98

    
99

    
100
def DumpSignedJson(data, key, salt=None):
101
  """Serialize a given object and authenticate it.
102

103
  @param data: the data to serialize
104
  @param key: shared hmac key
105
  @return: the string representation of data signed by the hmac key
106

107
  """
108
  txt = DumpJson(data, indent=False)
109
  if salt is None:
110
    salt = ''
111
  signed_dict = {
112
    'msg': txt,
113
    'salt': salt,
114
    'hmac': hmac.new(key, salt + txt, sha1).hexdigest(),
115
  }
116
  return DumpJson(signed_dict, indent=False)
117

    
118

    
119
def LoadSignedJson(txt, key):
120
  """Verify that a given message was signed with the given key, and load it.
121

122
  @param txt: json-encoded hmac-signed message
123
  @param key: shared hmac key
124
  @rtype: tuple of original data, string
125
  @return: original data, salt
126
  @raises errors.SignatureError: if the message signature doesn't verify
127

128
  """
129
  signed_dict = LoadJson(txt)
130
  if not isinstance(signed_dict, dict):
131
    raise errors.SignatureError('Invalid external message')
132
  try:
133
    msg = signed_dict['msg']
134
    salt = signed_dict['salt']
135
    hmac_sign = signed_dict['hmac']
136
  except KeyError:
137
    raise errors.SignatureError('Invalid external message')
138

    
139
  if hmac.new(key, salt + msg, sha1).hexdigest() != hmac_sign:
140
    raise errors.SignatureError('Invalid Signature')
141

    
142
  return LoadJson(msg), salt
143

    
144

    
145
Dump = DumpJson
146
Load = LoadJson
147
DumpSigned = DumpSignedJson
148
LoadSigned = LoadSignedJson