2 Records who we trust to sign interfaces.
4 @var trust_db: Singleton trust database instance.
7 # Copyright (C) 2006, Thomas Leonard
8 # See the README file for details, or visit http://0install.net.
13 from namespaces
import config_site
, config_prog
, XMLNS_TRUST
15 class TrustDB(object):
16 """A database of trusted keys.
17 @ivar keys: maps trusted key fingerprints to a list of domains
18 @type keys: {str: set(str)}
19 @ivar watchers: callbacks invoked by L{notify}
20 @see: L{trust_db} - the singleton instance of this class"""
21 __slots__
= ['keys', 'watchers']
27 def is_trusted(self
, fingerprint
, domain
= None):
28 self
.ensure_uptodate()
30 domains
= self
.keys
.get(fingerprint
, None)
31 if not domains
: return False # Unknown key
34 return True # Deprecated
36 return domain
in domains
or '*' in domains
38 def get_trust_domains(self
, fingerprint
):
39 """Return the set of domains in which this key is trusted.
40 If the list includes '*' then the key is trusted everywhere.
43 self
.ensure_uptodate()
44 return self
.keys
.get(fingerprint
, sets
.Set())
46 def get_keys_for_domain(self
, domain
):
47 """Return the set of keys trusted for this domain.
49 self
.ensure_uptodate()
50 return sets
.Set([fp
for fp
in self
.keys
51 if domain
in self
.keys
[fp
]])
53 def trust_key(self
, fingerprint
, domain
= '*'):
54 """Add key to the list of trusted fingerprints.
55 @param fingerprint: base 16 fingerprint without any spaces
56 @type fingerprint: str
57 @param domain: domain in which key is to be trusted
59 @note: call L{notify} after trusting one or more new keys"""
60 if self
.is_trusted(fingerprint
, domain
): return
62 int(fingerprint
, 16) # Ensure fingerprint is valid
64 if fingerprint
not in self
.keys
:
65 self
.keys
[fingerprint
] = sets
.Set()
68 # warn("Calling trust_key() without a domain is deprecated")
70 self
.keys
[fingerprint
].add(domain
)
73 def untrust_key(self
, key
, domain
= '*'):
74 self
.ensure_uptodate()
75 self
.keys
[key
].remove(domain
)
77 if not self
.keys
[key
]:
78 # No more domains for this key
84 from xml
.dom
import minidom
87 doc
= minidom
.Document()
88 root
= doc
.createElementNS(XMLNS_TRUST
, 'trusted-keys')
89 root
.setAttribute('xmlns', XMLNS_TRUST
)
92 for fingerprint
in self
.keys
:
93 keyelem
= doc
.createElementNS(XMLNS_TRUST
, 'key')
94 root
.appendChild(keyelem
)
95 keyelem
.setAttribute('fingerprint', fingerprint
)
96 for domain
in self
.keys
[fingerprint
]:
97 domainelem
= doc
.createElementNS(XMLNS_TRUST
, 'domain')
98 domainelem
.setAttribute('value', domain
)
99 keyelem
.appendChild(domainelem
)
101 d
= basedir
.save_config_path(config_site
, config_prog
)
102 fd
, tmpname
= tempfile
.mkstemp(dir = d
, prefix
= 'trust-')
103 tmp
= os
.fdopen(fd
, 'wb')
104 doc
.writexml(tmp
, indent
= "", addindent
= " ", newl
= "\n")
107 os
.rename(tmpname
, os
.path
.join(d
, 'trustdb.xml'))
110 """Call all watcher callbacks.
111 This should be called after trusting or untrusting one or more new keys.
113 for w
in self
.watchers
: w()
115 def ensure_uptodate(self
):
116 from xml
.dom
import minidom
118 # This is a bit inefficient... (could cache things)
121 trust
= basedir
.load_first_config(config_site
, config_prog
, 'trustdb.xml')
123 keys
= minidom
.parse(trust
).documentElement
124 for key
in keys
.getElementsByTagNameNS(XMLNS_TRUST
, 'key'):
126 self
.keys
[key
.getAttribute('fingerprint')] = domains
127 for domain
in key
.getElementsByTagNameNS(XMLNS_TRUST
, 'domain'):
128 domains
.add(domain
.getAttribute('value'))
130 # Convert old database to XML format
131 trust
= basedir
.load_first_config(config_site
, config_prog
, 'trust')
133 #print "Loading trust from", trust_db
134 for key
in file(trust
).read().split('\n'):
136 self
.keys
[key
] = sets
.Set('*')
138 def domain_from_url(url
):
139 """Extract the trust domain for a URL.
140 @param url: the feed's URL
142 @return: the trust domain
145 @raise SafeException: the URL can't be parsed"""
147 from zeroinstall
import SafeException
148 if url
.startswith('/'):
149 raise SafeException("Can't get domain from a local path: '%s'" % url
)
150 domain
= urlparse
.urlparse(url
)[1]
151 if domain
and domain
!= '*':
153 raise SafeException("Can't extract domain from URL '%s'" % url
)