Add descriptive comments, fix bugs.

Signed-off-by: Anant Prakash <anantprakashjsr@gmail.com>
This commit is contained in:
Anant Prakash 2018-03-16 11:20:34 +05:30
parent c168d2e38b
commit 1ba3b427ae
No known key found for this signature in database
GPG key ID: C5D399F626523045
5 changed files with 62 additions and 72 deletions

View file

@ -1,3 +1,5 @@
// Copyright 2018 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at

View file

@ -1,3 +1,5 @@
// Copyright 2018 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at

View file

@ -1,4 +1,4 @@
// Copyright 2018 Vector Creations Ltd // Copyright 2018 New Vector Ltd
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -26,11 +26,11 @@ import (
const ( const (
macaroonVersion = macaroon.V2 macaroonVersion = macaroon.V2
defaultDuration = 2 * 60 defaultDuration = 2 * 60
// UserPrefix for user_id caveat // UserPrefix is common prefix for every user_id caveat
UserPrefix = "user_id = " UserPrefix = "user_id = "
// TimePrefix for expiry caveat // TimePrefix is common prefix for every expiry caveat
TimePrefix = "time < " TimePrefix = "time < "
// Gen represents an unique identifier used as a caveat // Gen is a common caveat for every token
Gen = "gen = 1" Gen = "gen = 1"
) )
@ -61,14 +61,17 @@ func GenerateLoginToken(op TokenOptions) (string, error) {
if err != nil { if err != nil {
return "", macaroonError(err) return "", macaroonError(err)
} }
urlSafeEncode, err := SerializeMacaroon(*mac)
urlSafeEncode, err := serializeMacaroon(*mac) if err != nil {
return urlSafeEncode, macaroonError(err) return "", macaroonError(err)
}
return urlSafeEncode, nil
} }
// generateBaseMacaroon generates a base macaroon common for accessToken & loginToken // generateBaseMacaroon generates a base macaroon common for accessToken & loginToken.
// Returns a macaroon tied with userID, // Returns a macaroon tied with userID,
// returns formated error if something goes wrong. // returns an error if something goes wrong.
func generateBaseMacaroon( func generateBaseMacaroon(
secret []byte, ServerName string, userID string, secret []byte, ServerName string, userID string,
) (*macaroon.Macaroon, error) { ) (*macaroon.Macaroon, error) {
@ -94,9 +97,9 @@ func macaroonError(err error) error {
return fmt.Errorf("Macaroon creation failed: %s", err.Error()) return fmt.Errorf("Macaroon creation failed: %s", err.Error())
} }
// serializeMacaroon takes a macaroon to be serialized. // SerializeMacaroon takes a macaroon to be serialized.
// returns a base64 encoded string, URL safe, can be sent via web, email, etc. // returns it's base64 encoded string, URL safe, which can be sent via web, email, etc.
func serializeMacaroon(m macaroon.Macaroon) (string, error) { func SerializeMacaroon(m macaroon.Macaroon) (string, error) {
bin, err := m.MarshalBinary() bin, err := m.MarshalBinary()
if err != nil { if err != nil {
@ -107,19 +110,16 @@ func serializeMacaroon(m macaroon.Macaroon) (string, error) {
return urlSafeEncode, nil return urlSafeEncode, nil
} }
// deserializeMacaroon takes a base64 encoded string to deserialized. // DeSerializeMacaroon takes a base64 encoded string (of a macaroon, hopefully) to de-serialized.
// Returns a macaroon. On failure returns error with description. // Returns a macaroon. On failure returns error with description.
func deserializeMacaroon(urlSafeEncode string) (*macaroon.Macaroon, error) { func DeSerializeMacaroon(urlSafeEncode string) (*macaroon.Macaroon, error) {
bin, err := base64.RawURLEncoding.DecodeString(urlSafeEncode) bin, err := base64.RawURLEncoding.DecodeString(urlSafeEncode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var mac *macaroon.Macaroon var mac macaroon.Macaroon
err = mac.UnmarshalBinary(bin) err = mac.UnmarshalBinary(bin)
if err != nil { return &mac, err
return nil, err
}
return mac, nil
} }

View file

@ -1,4 +1,4 @@
// Copyright 2018 Vector Creations Ltd // Copyright 2018 New Vector Ltd
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -19,95 +19,93 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/util"
) )
// GetUserFromToken returns the user associated with the token // GetUserFromToken returns the user associated with the token
// Returns the error if something goes wrong. // Returns the error if something goes wrong.
// Warning: Does not validate the token. Use ValidateToken for that. // Warning: Does not validate the token. Use ValidateToken for that.
func GetUserFromToken(token string) (user string, err error) { func GetUserFromToken(token string) (user string, err error) {
mac, err := deserializeMacaroon(token) mac, err := DeSerializeMacaroon(token)
if err != nil { if err != nil {
return return
} }
// ID() returns a []byte, so convert it to string
user = string(mac.Id()[:]) user = string(mac.Id()[:])
return return
} }
// ValidateToken validates that the Token is understood and was signed by this server. // ValidateToken validates that the Token is understood and was signed by this server.
// Returns nil if token is valid, otherwise returns a error response which can be sent to client. // Returns nil if token is valid, otherwise returns an error/
func ValidateToken(op TokenOptions, token string) *util.JSONResponse { func ValidateToken(op TokenOptions, token string) error {
macaroon, err := deserializeMacaroon(token) macaroon, err := DeSerializeMacaroon(token)
if err != nil { if err != nil {
return &util.JSONResponse{ return errors.New("Token does not represent a valid macaroon")
Code: 401,
JSON: jsonerror.UnknownToken("Token does not represent a valid macaroon"),
}
} }
// VerifySignature returns all caveats in the macaroon
caveats, err := macaroon.VerifySignature(op.ServerMacaroonSecret, nil) caveats, err := macaroon.VerifySignature(op.ServerMacaroonSecret, nil)
if err != nil { if err != nil {
return &util.JSONResponse{ return errors.New("Provided token was not issued by this server")
Code: 401,
JSON: jsonerror.UnknownToken("Provided token was not issued by this server"),
}
} }
err = verifyCaveats(caveats, op.UserID) err = verifyCaveats(caveats, op.UserID)
if err != nil { if err != nil {
return &util.JSONResponse{ return errors.New("Provided token not authorized")
Code: 403,
JSON: jsonerror.Forbidden("Provided token not authorized"),
}
} }
return nil return nil
} }
// verifyCaveats verifies caveats associated with a login token macaroon. // verifyCaveats verifies caveats associated with a login macaroon token.
// which are "gen = 1", "user_id = ...", "time < ..." // Which are "gen = 1", "user_id = ...", "time < ..."
// Returns nil on successful verification, else returns the error. // Returns nil on successful verification, else returns an error.
func verifyCaveats(caveats []string, userID string) error { func verifyCaveats(caveats []string, userID string) error {
// let last 4 bit be Uabc where, // verified is a bitmask
// U: unknownCaveat // let last 4 bit be uabc where,
// a, b, c: 1st, 2nd & 3rd caveat to be verified respectively. // u denotes any unknownCaveat
// a, b, c denotes the three caveats to be verified respectively.
var verified uint8 var verified uint8
now := time.Now().Second() now := time.Now().Second()
for _, caveat := range caveats { for _, caveat := range caveats {
switch { switch {
case caveat == Gen: case caveat == Gen:
// "gen = 1"
verified |= 1 verified |= 1
case strings.HasPrefix(caveat, UserPrefix): case strings.HasPrefix(caveat, UserPrefix):
// "user_id = ..."
if caveat[len(UserPrefix):] == userID { if caveat[len(UserPrefix):] == userID {
verified |= 2 verified |= 2
} }
case strings.HasPrefix(caveat, TimePrefix): case strings.HasPrefix(caveat, TimePrefix):
// "time < ..."
if verifyExpiry(caveat[len(TimePrefix):], now) { if verifyExpiry(caveat[len(TimePrefix):], now) {
verified |= 4 verified |= 4
} }
default: default:
// Unknown caveat
verified |= 8 verified |= 8
break
} }
} }
// Check that all three caveats are verified and no extra caveats // Check that all three caveats are verified and no extra caveats are present
// i.e. Uabc == 0111 // i.e. uabc == 0111, which implies verified == 7
if verified == 7 { if verified != 7 {
return nil if verified >= 8 {
} else if verified >= 8 {
return errors.New("Unknown caveat present") return errors.New("Unknown caveat present")
} }
return errors.New("Required caveats not present") return errors.New("Required caveats not present")
} }
return nil
}
// verifyExpiry verifies an expiry caveat
func verifyExpiry(t string, now int) bool { func verifyExpiry(t string, now int) bool {
expiry, err := strconv.Atoi(t) expiry, err := strconv.Atoi(t)

View file

@ -141,22 +141,6 @@ func handlePasswordLogin(
} }
} }
var token string
token, err = tokens.GenerateLoginToken(tokens.TokenOptions{
ServerMacaroonSecret: []byte(string(cfg.Matrix.PrivateKey)),
ServerName: string(cfg.Matrix.ServerName),
UserID: r.User,
})
if err != nil {
return util.JSONResponse{
Code: http.StatusNotImplemented,
JSON: jsonerror.Unknown("Token generation failed"),
}
}
util.GetLogger(req.Context()).WithField("user", r.User).Info(token)
return completeLogin(r, localpart, deviceDB, req, cfg) return completeLogin(r, localpart, deviceDB, req, cfg)
} }
@ -206,15 +190,19 @@ func handleTokenLogin(
} }
tokenOptions := tokens.TokenOptions{ tokenOptions := tokens.TokenOptions{
ServerMacaroonSecret: []byte(string(cfg.Matrix.PrivateKey)), // ServerMacaroonSecret should be a []byte
ServerMacaroonSecret: []byte(cfg.Matrix.PrivateKey),
ServerName: string(cfg.Matrix.ServerName), ServerName: string(cfg.Matrix.ServerName),
UserID: userID, UserID: userID,
} }
resErr := tokens.ValidateToken(tokenOptions, r.Token) err := tokens.ValidateToken(tokenOptions, r.Token)
if resErr != nil { if err != nil {
return *resErr return util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: jsonerror.UnknownToken(err.Error()),
}
} }
return completeLogin(r, localpart, deviceDB, req, cfg) return completeLogin(r, localpart, deviceDB, req, cfg)