Browse Source

Playing around with keygen element.

pull/28/head
Simon Eisenmann 12 years ago
parent
commit
69039af618
  1. 174
      src/app/spreed-speakfreely-server/users.go
  2. 4
      static/js/directives/settings.js
  3. 67
      static/js/services/mediastream.js
  4. 28
      static/partials/settings.html

174
src/app/spreed-speakfreely-server/users.go

@ -23,14 +23,22 @@ package main
import ( import (
"crypto/hmac" "crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/sha256" "crypto/sha256"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"encoding/pem"
"errors" "errors"
"fmt" "fmt"
"github.com/longsleep/pkac"
"github.com/satori/go.uuid" "github.com/satori/go.uuid"
"github.com/strukturag/phoenix" "github.com/strukturag/phoenix"
"io/ioutil"
"log" "log"
"math/big"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -111,6 +119,115 @@ func (uh *UsersHTTPHeaderHandler) Create(un *UserNonce, request *http.Request) (
} }
func loadPEMfromFile(fn string) (block *pem.Block, err error) {
data, err := ioutil.ReadFile(fn)
if err != nil {
return
}
block, _ = pem.Decode(data)
return block, nil
}
type UsersCertificateHandler struct {
validFor time.Duration
privateKey *rsa.PrivateKey
certificate *x509.Certificate
}
func (uh *UsersCertificateHandler) loadPrivateKey(fn string) error {
pemBlock, err := loadPEMfromFile(fn)
if err != nil {
return err
}
privateKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
if err != nil {
return err
}
uh.privateKey = privateKey
log.Printf("Users certificate private key loaded from %s\n", fn)
return nil
}
func (uh *UsersCertificateHandler) loadCertificate(fn string) error {
pemBlock, err := loadPEMfromFile(fn)
if err != nil {
return err
}
certificates, err := x509.ParseCertificates(pemBlock.Bytes)
if err != nil {
return err
}
uh.certificate = certificates[0]
log.Printf("Users certificate loaded from %s\n", fn)
return nil
}
func (uh *UsersCertificateHandler) makeTemplate(serialNumber string) *x509.Certificate {
notBefore := time.Now()
notAfter := notBefore.Add(uh.validFor)
return &x509.Certificate{
SerialNumber: big.NewInt(42),
Subject: pkix.Name{
SerialNumber: serialNumber,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: false,
}
}
func (uh *UsersCertificateHandler) Validate(snr *SessionNonceRequest, request *http.Request) (string, error) {
return "", errors.New("certificate validation not implemented")
}
func (uh *UsersCertificateHandler) Create(un *UserNonce, request *http.Request) (*UserNonce, error) {
spkac := request.Form.Get("pubkey")
if spkac == "" {
return nil, errors.New("no spkac provided")
}
spkacDerBytes, err := base64.StdEncoding.DecodeString(spkac)
if err != nil {
return nil, errors.New(fmt.Sprintf("spkac invalid: %s", err))
}
publicKey, err := pkac.ParseSPKAC(spkacDerBytes)
if err != nil {
return nil, errors.New(fmt.Sprintf("unable to parse spkac: %s", err))
}
template := uh.makeTemplate(un.Userid)
certDerBytes, err := x509.CreateCertificate(rand.Reader, template, uh.certificate, publicKey, uh.privateKey)
if err != nil {
return nil, errors.New(fmt.Sprintf("failed to create certificate: %s", err))
}
log.Println("Generated new certificate", un.Userid)
un.SetResponse(certDerBytes, "application/x-x509-user-cert", http.Header{
"Content-Length": {strconv.Itoa(len(certDerBytes))},
"Accept-Ranges": {"bytes"},
"Last-Modified": {time.Now().UTC().Format(http.TimeFormat)},
})
return un, nil
}
type UserNonce struct { type UserNonce struct {
Nonce string `json:"nonce"` Nonce string `json:"nonce"`
Userid string `json:"userid"` Userid string `json:"userid"`
@ -118,6 +235,28 @@ type UserNonce struct {
Secret string `json:"secret"` Secret string `json:"secret"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
Success bool `json:"success"` Success bool `json:"success"`
raw []byte
contentType string
header http.Header
}
func (un *UserNonce) SetResponse(raw []byte, contentType string, header http.Header) {
un.raw = raw
un.contentType = contentType
un.header = header
}
func (un *UserNonce) Response() (int, interface{}, http.Header) {
header := un.header
if header == nil {
header = http.Header{}
}
if un.contentType != "" {
header.Set("Content-Type", un.contentType)
return 200, un.raw, header
} else {
return 200, un, header
}
} }
type Users struct { type Users struct {
@ -143,6 +282,18 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users {
headerName = "x-users" headerName = "x-users"
} }
handler = &UsersHTTPHeaderHandler{headerName: headerName} handler = &UsersHTTPHeaderHandler{headerName: headerName}
case "certificate":
uh := &UsersCertificateHandler{}
uh.validFor = 24 * time.Hour * 365
keyFn, _ := runtime.GetString("users", "certificate_key")
certificateFn, _ := runtime.GetString("users", "certificate_certificate")
if keyFn != "" && certificateFn != "" {
uh.loadPrivateKey(keyFn)
}
if certificateFn != "" {
uh.loadCertificate(certificateFn)
}
handler = uh
default: default:
mode = "" mode = ""
} }
@ -164,11 +315,22 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users {
// Post is used to create new userids for this server. // Post is used to create new userids for this server.
func (users *Users) Post(request *http.Request) (int, interface{}, http.Header) { func (users *Users) Post(request *http.Request) (int, interface{}, http.Header) {
decoder := json.NewDecoder(request.Body) var snr *SessionNonceRequest
var snr SessionNonceRequest
err := decoder.Decode(&snr) switch request.Header.Get("Content-Type") {
if err != nil { case "application/json":
return 400, NewApiError("users_bad_request", "Failed to parse request"), http.Header{"Content-Type": {"application/json"}} decoder := json.NewDecoder(request.Body)
err := decoder.Decode(snr)
if err != nil {
return 400, NewApiError("users_bad_request", "Failed to parse request"), http.Header{"Content-Type": {"application/json"}}
}
case "application/x-www-form-urlencoded":
snr = &SessionNonceRequest{
Id: request.Form.Get("id"),
Sid: request.Form.Get("sid"),
}
default:
return 400, NewApiError("users_invalid_request", "Invalid request type"), http.Header{"Content-Type": {"application/json"}}
} }
// Make sure that we have a Sid. // Make sure that we have a Sid.
@ -195,6 +357,6 @@ func (users *Users) Post(request *http.Request) (int, interface{}, http.Header)
} }
log.Printf("Users create successfull %s -> %s\n", snr.Id, un.Userid) log.Printf("Users create successfull %s -> %s\n", snr.Id, un.Userid)
return 200, un, http.Header{"Content-Type": {"application/json"}} return un.Response()
} }

4
static/js/directives/settings.js

@ -127,10 +127,10 @@ define(['underscore', 'text!partials/settings.html'], function(_, template) {
} }
}; };
$scope.registerUserid = function() { $scope.registerUserid = function(btn) {
console.log("No userid - creating one ..."); console.log("No userid - creating one ...");
mediaStream.users.register(function(data) { mediaStream.users.register(btn.form, function(data) {
console.info("Created new userid:", data.userid); console.info("Created new userid:", data.userid);
if (data.nonce) { if (data.nonce) {
// If the server provided us a nonce, we can do everthing on our own. // If the server provided us a nonce, we can do everthing on our own.

67
static/js/services/mediastream.js

@ -65,32 +65,51 @@ define([
} }
}, },
users: { users: {
register: function(success_cb, error_cb) { register: function(form, success_cb, error_cb) {
var url = mediaStream.url.api("users"); var url = mediaStream.url.api("users");
var data = { if (form) {
id: mediaStream.api.id, $(form).attr("action", url).attr("method", "POST");
sid: mediaStream.api.sid var idE = $('<input name="id" type="hidden">');
} idE.val(mediaStream.api.id);
$http({ var sidE = $('<input name="sid" type="hidden">');
method: "POST", sidE.val(mediaStream.api.sid);
url: url, $(form).append(idE);
data: JSON.stringify(data), $(form).append(sidE);
headers: {'Content-Type': 'application/json'} var iframe = $(form).find("iframe");
}). console.log("xxxx", iframe[0]);
success(function(data, status) { form.submit();
if (data.userid !== "" && data.success) { $timeout(function() {
success_cb(data, status); idE.remove();
} else { sidE.remove();
if (error_cb) { idE=null;
error_cb(data, status); sidE=null;
} }, 0);
} else {
var data = {
id: mediaStream.api.id,
sid: mediaStream.api.sid
} }
}). $http({
error(function(data, status) { method: "POST",
if (error_cb) { url: url,
error_cb(data, status) data: JSON.stringify(data),
} headers: {'Content-Type': 'application/json'}
}); }).
success(function(data, status) {
if (data.userid !== "" && data.success) {
success_cb(data, status);
} else {
if (error_cb) {
error_cb(data, status);
}
}
}).
error(function(data, status) {
if (error_cb) {
error_cb(data, status)
}
});
}
}, },
authorize: function(data, success_cb, error_cb) { authorize: function(data, success_cb, error_cb) {
var url = mediaStream.url.api("sessions") + "/" + mediaStream.api.id + "/"; var url = mediaStream.url.api("sessions") + "/" + mediaStream.api.id + "/";

28
static/partials/settings.html

@ -1,6 +1,6 @@
<div class="settings nicescroll"> <div class="settings nicescroll">
<div class="version">{{version}}</div> <div class="version">{{version}}</div>
<form class="form-horizontal" on-enter="saveSettings(user)" on-escape="reset()" <div class="form-horizontal" on-enter="saveSettings(user)" on-escape="reset()"
<fieldset> <fieldset>
<legend>{{_('Settings')}}</legend> <legend>{{_('Settings')}}</legend>
@ -27,17 +27,31 @@
</div> </div>
</div> </div>
<div class="form-group" ng-if="withUsers || userid"> <div class="form-group" ng-if="withUsers || userid">
<label class="col-xs-4 control-label">{{_('Your ID')}} {{serverCfg|json}}</label> <label class="col-xs-4 control-label">{{_('Your ID')}}</label>
<div class="col-xs-8">
<form class="col-xs-8" target="users_registration_certificate_iframe">
<keygen style="display:none" name="pubkey"/>
<label ng-if="!userid && withUsersRegistration"> <label ng-if="!userid && withUsersRegistration">
<a class="btn btn-small btn-primary" ng-click="registerUserid()">{{_('Register')}}</a> <button class="btn btn-small btn-primary" ng-click="registerUserid($event.target)">{{_('Register')}}</button>
</label>
<pre class="small" ng-if="userid">{{userid}}</pre>
<iframe style="display:none" name="users_registration_certificate_iframe"></iframe>
</form>
<div ng-if="false" class="col-xs-8">
<label ng-if="!userid && withUsersRegistration">
<button class="btn btn-small btn-primary" ng-click="registerUserid($event.target)">{{_('Register')}}</button>
</label> </label>
<span class="help-block" ng-if="!userid && withUsersRegistration">{{_('Only register an ID if this is your private browser.')}}</span>
<pre class="small" ng-if="userid">{{userid}}</pre> <pre class="small" ng-if="userid">{{userid}}</pre>
<label ng-if="userid && loadedUserlogin"> <label ng-if="userid && loadedUserlogin">
<a class="btn btn-small btn-default" ng-click="forgetUserid()">{{_('Log out')}}</a> <button class="btn btn-small btn-default" ng-click="forgetUserid()">{{_('Log out')}}</button>
</label> </label>
</div> </div>
<div class="col-xs-8 col-xs-offset-4" ng-if="!userid && withUsersRegistration">
<span class="help-block">{{_('Only register an ID if this is your private browser.')}}</span>
</div>
</div> </div>
<hr/> <hr/>
<div ng-show="mediaSources.supported" class="form-group"> <div ng-show="mediaSources.supported" class="form-group">
@ -152,5 +166,5 @@
</div> </div>
</fieldset> </fieldset>
</form> </div>
</div> </div>

Loading…
Cancel
Save