mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-12 17:33:09 -06:00
Add descriptive comments, fix bugs.
Signed-off-by: Anant Prakash <anantprakashjsr@gmail.com>
This commit is contained in:
parent
c168d2e38b
commit
1ba3b427ae
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue