mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-12 09:23:09 -06:00
Merge e28e95963b into 7ac1efc4b3
This commit is contained in:
commit
1f9d7b27f4
|
|
@ -0,0 +1,115 @@
|
|||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tokens
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
// GetUserFromToken returns the user associated with the token
|
||||
// Returns the error if something goes wrong.
|
||||
// Warning: Does not validate the token. Use ValidateToken for that.
|
||||
func GetUserFromToken(token string) (user string, err error) {
|
||||
mac, err := deSerializeMacaroon(token)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
user = string(mac.Id()[:])
|
||||
return
|
||||
}
|
||||
|
||||
// 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.
|
||||
func ValidateToken(op TokenOptions, token string) *util.JSONResponse {
|
||||
macaroon, err := deSerializeMacaroon(token)
|
||||
if err != nil {
|
||||
return &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.UnknownToken("Token does not represent a valid macaroon"),
|
||||
}
|
||||
}
|
||||
|
||||
caveats, err := macaroon.VerifySignature(op.ServerPrivateKey, nil)
|
||||
if err != nil {
|
||||
return &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.UnknownToken("Provided token was not issued by this server"),
|
||||
}
|
||||
}
|
||||
|
||||
err = verifyCaveats(caveats, op.UserID)
|
||||
if err != nil {
|
||||
return &util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: jsonerror.Forbidden("Provided token not authorized"),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyCaveats verifies caveats associated with a login token macaroon.
|
||||
// which are "gen = 1", "user_id = ...", "time < ..."
|
||||
// Returns nil on successful verification, else returns an error.
|
||||
func verifyCaveats(caveats []string, userID string) error {
|
||||
// variable verified represents a bitmap
|
||||
// last 4 bit is Uvvv where,
|
||||
// U: unknownCaveat
|
||||
// v: caveat to be verified
|
||||
var verified uint8
|
||||
now := time.Now().Second()
|
||||
|
||||
LoopCaveat:
|
||||
for _, caveat := range caveats {
|
||||
switch {
|
||||
case caveat == Gen:
|
||||
verified |= 1
|
||||
case strings.HasPrefix(caveat, UserPrefix):
|
||||
if caveat[len(UserPrefix):] == userID {
|
||||
verified |= 2
|
||||
}
|
||||
case strings.HasPrefix(caveat, TimePrefix):
|
||||
if verifyExpiry(caveat[len(TimePrefix):], now) {
|
||||
verified |= 4
|
||||
}
|
||||
default:
|
||||
verified |= 8
|
||||
break LoopCaveat
|
||||
}
|
||||
}
|
||||
// Check that all three caveats are verified and no extra caveats
|
||||
// i.e. Uvvv == 0111
|
||||
if verified == 7 {
|
||||
return nil
|
||||
} else if verified >= 8 {
|
||||
return errors.New("Unknown caveat present")
|
||||
}
|
||||
|
||||
return errors.New("Required caveats not present")
|
||||
}
|
||||
|
||||
func verifyExpiry(t string, now int) bool {
|
||||
expiry, err := strconv.Atoi(t)
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return now < expiry
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tokens
|
||||
|
||||
import "testing"
|
||||
|
||||
// If any of these options are missing, validation should fail
|
||||
var invalidMissings = []string{"ServerPrivateKey", "UserID"}
|
||||
|
||||
func TestValidateToken(t *testing.T) {
|
||||
fakeToken, err := GenerateLoginToken(validTokenOp)
|
||||
if err != nil {
|
||||
t.Errorf("Token generation failed for valid TokenOptions with err: %s", err.Error())
|
||||
}
|
||||
|
||||
// Test validation
|
||||
res := ValidateToken(validTokenOp, fakeToken)
|
||||
if res != nil {
|
||||
t.Error("Token validation failed with response: ", *res)
|
||||
}
|
||||
|
||||
// Test validation fails for invalid TokenOp
|
||||
for _, invalidMissing := range invalidMissings {
|
||||
res = ValidateToken(invalidTokenOps[invalidMissing], fakeToken)
|
||||
if res == nil {
|
||||
t.Errorf("Token validation should fail for TokenOptions with missing %s", invalidMissing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserFromToken(t *testing.T) {
|
||||
fakeToken, err := GenerateLoginToken(validTokenOp)
|
||||
if err != nil {
|
||||
t.Errorf("Token generation failed for valid TokenOptions with err: %s", err.Error())
|
||||
}
|
||||
|
||||
// Test validation
|
||||
name, err := GetUserFromToken(fakeToken)
|
||||
if err != nil {
|
||||
t.Error("Failed to get userID from Token: ", err)
|
||||
}
|
||||
|
||||
if name != validTokenOp.UserID {
|
||||
t.Error("UserID from Token doesn't match, got: ", name, " expected: ", validTokenOp.UserID)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue