Commit 2c10dc67 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

irma_configuration and scheme manager downloading

parent 90b3a53b
......@@ -10,6 +10,11 @@ import (
"crypto/sha256"
"fmt"
"strings"
"github.com/go-errors/errors"
"github.com/mhe/gabi"
)
......@@ -267,6 +272,100 @@ func (store *ConfigurationStore) Copy(source string, parse bool) error {
return nil
}
func (store *ConfigurationStore) Download(set *IrmaIdentifierSet) error {
func (store *ConfigurationStore) DownloadSchemeManager(url string) (*SchemeManager, error) {
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
url = "https://" + url
}
if url[len(url)-1] == '/' {
url = url[:len(url)-1]
}
if strings.HasSuffix(url, "/description.xml") {
url = url[:len(url)-len("/description.xml")]
}
b, err := NewHTTPTransport(url).GetBytes("/description.xml")
if err != nil {
return nil, err
}
manager := &SchemeManager{}
if err = xml.Unmarshal(b, manager); err != nil {
return nil, err
}
manager.URL = url // TODO?
return manager, nil
}
func (store *ConfigurationStore) RemoveSchemeManager(id SchemeManagerIdentifier) error {
// Remove everything falling under the manager's responsibility
for credid := range store.Credentials {
if credid.IssuerIdentifier().SchemeManagerIdentifier() == id {
delete(store.Credentials, credid)
}
}
for issid := range store.Issuers {
if issid.SchemeManagerIdentifier() == id {
delete(store.Issuers, issid)
}
}
for issid := range store.publicKeys {
if issid.SchemeManagerIdentifier() == id {
delete(store.publicKeys, issid)
}
}
// Remove from storage
return os.RemoveAll(fmt.Sprintf("%s/%s", store.path, id.String()))
// or, remove above iterations and call .ParseFolder()?
}
func (store *ConfigurationStore) AddSchemeManager(manager *SchemeManager) error {
name := manager.ID
if err := ensureDirectoryExists(fmt.Sprintf("%s/%s", store.path, name)); err != nil {
return err
}
b, err := xml.Marshal(manager)
if err != nil {
return err
}
if err := saveFile(fmt.Sprintf("%s/%s/description.xml", store.path, name), b); err != nil {
return err
}
store.SchemeManagers[NewSchemeManagerIdentifier(name)] = manager
return nil
}
func (store *ConfigurationStore) Download(set *IrmaIdentifierSet) error {
var contains bool
for manid := range set.SchemeManagers {
if _, contains = store.SchemeManagers[manid]; !contains {
return errors.Errorf("Unknown scheme manager: %s", manid)
}
}
transport := NewHTTPTransport("")
for issid := range set.Issuers {
if _, contains = store.Issuers[issid]; !contains {
url := store.SchemeManagers[issid.SchemeManagerIdentifier()].URL + "/" + issid.Name()
path := fmt.Sprintf("%s/%s/%s", store.path, issid.SchemeManagerIdentifier().String(), issid.Name())
transport.GetFile(url+"/description.xml", path+"/description.xml")
transport.GetFile(url+"/logo.png", path+"/logo.png")
}
for issid, list := range set.PublicKeys {
for _, count := range list {
manager := issid.SchemeManagerIdentifier()
suffix := fmt.Sprintf("/%s/PublicKeys/%d.xml", issid.Name(), count)
path := fmt.Sprintf("%s/%s/%s", store.path, manager.String(), suffix)
transport.GetFile(store.SchemeManagers[manager].URL+suffix, path)
}
}
}
for credid := range set.CredentialTypes {
if _, contains := store.Credentials[credid]; !contains {
manager := credid.IssuerIdentifier().SchemeManagerIdentifier()
suffix := fmt.Sprintf("/%s/Issues/%s/description.xml", credid.IssuerIdentifier().Name(), credid.Name())
path := fmt.Sprintf("%s/%s/%s", store.path, manager.String(), suffix)
transport.GetFile(store.SchemeManagers[manager].URL+suffix, path)
}
}
return store.ParseFolder()
}
......@@ -437,3 +437,12 @@ func TestCredentialRemoval(t *testing.T) {
teardown(t)
}
func TestDownloadSchemeManager(t *testing.T) {
manager := parseStorage(t)
require.NoError(t, manager.ConfigurationStore.RemoveSchemeManager(NewSchemeManagerIdentifier("irma-demo")))
url := "https://raw.githubusercontent.com/credentials/irma_configuration/translate/irma-demo"
sm, err := manager.ConfigurationStore.DownloadSchemeManager(url)
require.NoError(t, err)
require.NotNil(t, sm)
}
......@@ -49,6 +49,7 @@ type CredentialManager struct {
ConfigurationStore *ConfigurationStore
irmaConfigurationPath string
androidStoragePath string
keyshareHandler KeyshareHandler
}
type secretKey struct {
......@@ -81,25 +82,25 @@ func NewCredentialManager(
return nil, err
}
var store *ConfigurationStore
if store, err = NewConfigurationStore(storagePath+"/irma_configuration", irmaConfigurationPath); err != nil {
return nil, err
}
if err = store.ParseFolder(); err != nil {
return nil, err
}
cm := &CredentialManager{
credentials: make(map[CredentialTypeIdentifier]map[int]*credential),
keyshareServers: make(map[SchemeManagerIdentifier]*keyshareServer),
attributes: make(map[CredentialTypeIdentifier][]*AttributeList),
irmaConfigurationPath: irmaConfigurationPath,
androidStoragePath: androidStoragePath,
ConfigurationStore: store,
storage: storage{storagePath: storagePath, ConfigurationStore: store},
keyshareHandler: keyshareHandler,
}
cm.ConfigurationStore, err = NewConfigurationStore(storagePath+"/irma_configuration", irmaConfigurationPath)
if err != nil {
return nil, err
}
if err = cm.ConfigurationStore.ParseFolder(); err != nil {
return nil, err
}
// Ensure storage path exists, and populate it with necessary files
cm.storage = storage{storagePath: storagePath, ConfigurationStore: cm.ConfigurationStore}
if err = cm.storage.EnsureStorageExists(); err != nil {
return nil, err
}
......
......@@ -7,6 +7,7 @@ import (
"io"
"io/ioutil"
"net/http"
"path/filepath"
"strings"
"time"
)
......@@ -23,7 +24,7 @@ const verbose = false
// NewHTTPTransport returns a new HTTPTransport.
func NewHTTPTransport(serverURL string) *HTTPTransport {
url := serverURL
if !strings.HasSuffix(url, "/") {
if serverURL != "" && !strings.HasSuffix(url, "/") { // TODO fix this
url += "/"
}
return &HTTPTransport{
......@@ -40,7 +41,34 @@ func (transport *HTTPTransport) SetHeader(name, val string) {
transport.headers[name] = val
}
func (transport *HTTPTransport) request(url string, method string, result interface{}, object interface{}) error {
func (transport *HTTPTransport) request(
url string, method string, reader io.Reader, isstr bool,
) (response *http.Response, err error) {
req, err := http.NewRequest(method, transport.Server+url, reader)
if err != nil {
return nil, &SessionError{ErrorType: ErrorTransport, Err: err}
}
req.Header.Set("User-Agent", "irmago")
if reader != nil {
if isstr {
req.Header.Set("Content-Type", "text/plain; charset=UTF-8")
} else {
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
}
}
for name, val := range transport.headers {
req.Header.Set(name, val)
}
res, err := transport.client.Do(req)
if err != nil {
return nil, &SessionError{ErrorType: ErrorTransport, Err: err}
}
return res, nil
}
func (transport *HTTPTransport) jsonRequest(url string, method string, result interface{}, object interface{}) error {
if method != http.MethodPost && method != http.MethodGet && method != http.MethodDelete {
panic("Unsupported HTTP method " + method)
}
......@@ -70,28 +98,10 @@ func (transport *HTTPTransport) request(url string, method string, result interf
}
}
req, err := http.NewRequest(method, transport.Server+url, reader)
res, err := transport.request(url, method, reader, isstr)
if err != nil {
return &SessionError{ErrorType: ErrorTransport, Err: err}
return err
}
req.Header.Set("User-Agent", "irmago")
if object != nil {
if isstr {
req.Header.Set("Content-Type", "text/plain; charset=UTF-8")
} else {
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
}
}
for name, val := range transport.headers {
req.Header.Set(name, val)
}
res, err := transport.client.Do(req)
if err != nil {
return &SessionError{ErrorType: ErrorTransport, Err: err}
}
if method == http.MethodDelete {
return nil
}
......@@ -127,17 +137,40 @@ func (transport *HTTPTransport) request(url string, method string, result interf
return nil
}
func (transport *HTTPTransport) GetBytes(url string) ([]byte, error) {
res, err := transport.request(url, http.MethodGet, nil, false)
if err != nil {
return nil, &SessionError{ErrorType: ErrorTransport, Err: err}
}
b, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, &SessionError{ErrorType: ErrorServerResponse, Err: err, Status: res.StatusCode}
}
return b, nil
}
func (transport *HTTPTransport) GetFile(url string, dest string) error {
b, err := transport.GetBytes(url)
if err != nil {
return err
}
if err = ensureDirectoryExists(filepath.Dir(dest)); err != nil {
return err
}
return saveFile(dest, b)
}
// Post sends the object to the server and parses its response into result.
func (transport *HTTPTransport) Post(url string, result interface{}, object interface{}) error {
return transport.request(url, http.MethodPost, result, object)
return transport.jsonRequest(url, http.MethodPost, result, object)
}
// Get performs a GET request and parses the server's response into result.
func (transport *HTTPTransport) Get(url string, result interface{}) error {
return transport.request(url, http.MethodGet, result, nil)
return transport.jsonRequest(url, http.MethodGet, result, nil)
}
// Delete performs a DELETE.
func (transport *HTTPTransport) Delete() {
_ = transport.request("", http.MethodDelete, nil, nil)
_ = transport.jsonRequest("", http.MethodDelete, nil, nil)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment