mirror of
https://github.com/pgsty/minio.git
synced 2026-08-09 07:43:29 +03:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d0f4cc89a5 | |||
| d65debb6bc | |||
| 72daccd468 | |||
| b363400587 | |||
| 6b41f941b6 | |||
| b9f5f9ba3f | |||
| c8ffa59d28 | |||
| 1141187bf2 |
+1
-1
@@ -90,7 +90,7 @@ func (api objectAPIHandlers) PutBucketACLHandler(w http.ResponseWriter, r *http.
|
||||
if aclHeader == "" {
|
||||
acl := &accessControlPolicy{}
|
||||
if err = xmlDecoder(r.Body, acl, r.ContentLength); err != nil {
|
||||
if err == io.EOF {
|
||||
if terr, ok := err.(*xml.SyntaxError); ok && terr.Msg == io.EOF.Error() {
|
||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingSecurityHeader),
|
||||
r.URL)
|
||||
return
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
@@ -60,13 +61,43 @@ func activeListeners() int {
|
||||
return int(globalHTTPListen.Subscribers()) + int(globalTrace.Subscribers())
|
||||
}
|
||||
|
||||
func waitForLowHTTPReq() {
|
||||
var currentIO func() int
|
||||
if httpServer := newHTTPServerFn(); httpServer != nil {
|
||||
currentIO = httpServer.GetRequestCount
|
||||
func waitForLowIO(maxIO int, maxWait time.Duration, currentIO func() int) {
|
||||
// No need to wait run at full speed.
|
||||
if maxIO <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
globalHealConfig.Wait(currentIO, activeListeners)
|
||||
const waitTick = 100 * time.Millisecond
|
||||
|
||||
tmpMaxWait := maxWait
|
||||
|
||||
for currentIO() >= maxIO {
|
||||
if tmpMaxWait > 0 {
|
||||
if tmpMaxWait < waitTick {
|
||||
time.Sleep(tmpMaxWait)
|
||||
} else {
|
||||
time.Sleep(waitTick)
|
||||
}
|
||||
tmpMaxWait -= waitTick
|
||||
}
|
||||
if tmpMaxWait <= 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func currentHTTPIO() int {
|
||||
httpServer := newHTTPServerFn()
|
||||
if httpServer == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return httpServer.GetRequestCount() - activeListeners()
|
||||
}
|
||||
|
||||
func waitForLowHTTPReq() {
|
||||
maxIO, maxWait, _ := globalHealConfig.Clone()
|
||||
waitForLowIO(maxIO, maxWait, currentHTTPIO)
|
||||
}
|
||||
|
||||
func initBackgroundHealing(ctx context.Context, objAPI ObjectLayer) {
|
||||
|
||||
+63
-24
@@ -21,6 +21,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -40,6 +41,8 @@ import (
|
||||
|
||||
// BucketMetadataSys captures all bucket metadata for a given cluster.
|
||||
type BucketMetadataSys struct {
|
||||
objAPI ObjectLayer
|
||||
|
||||
sync.RWMutex
|
||||
metadataMap map[string]BucketMetadata
|
||||
}
|
||||
@@ -386,39 +389,41 @@ func (sys *BucketMetadataSys) Init(ctx context.Context, buckets []BucketInfo, ob
|
||||
return errServerNotInitialized
|
||||
}
|
||||
|
||||
sys.objAPI = objAPI
|
||||
|
||||
// Load bucket metadata sys in background
|
||||
go sys.load(ctx, buckets, objAPI)
|
||||
go sys.init(ctx, buckets)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sys *BucketMetadataSys) loadBucketMetadata(ctx context.Context, bucket BucketInfo) error {
|
||||
meta, err := loadBucketMetadata(ctx, sys.objAPI, bucket.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sys.Lock()
|
||||
sys.metadataMap[bucket.Name] = meta
|
||||
sys.Unlock()
|
||||
|
||||
globalEventNotifier.set(bucket, meta) // set notification targets
|
||||
globalBucketTargetSys.set(bucket, meta) // set remote replication targets
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// concurrently load bucket metadata to speed up loading bucket metadata.
|
||||
func (sys *BucketMetadataSys) concurrentLoad(ctx context.Context, buckets []BucketInfo, objAPI ObjectLayer) {
|
||||
func (sys *BucketMetadataSys) concurrentLoad(ctx context.Context, buckets []BucketInfo) {
|
||||
g := errgroup.WithNErrs(len(buckets))
|
||||
for index := range buckets {
|
||||
index := index
|
||||
g.Go(func() error {
|
||||
_, _ = objAPI.HealBucket(ctx, buckets[index].Name, madmin.HealOpts{
|
||||
_, _ = sys.objAPI.HealBucket(ctx, buckets[index].Name, madmin.HealOpts{
|
||||
// Ensure heal opts for bucket metadata be deep healed all the time.
|
||||
ScanMode: madmin.HealDeepScan,
|
||||
Recreate: true,
|
||||
})
|
||||
meta, err := loadBucketMetadata(ctx, objAPI, buckets[index].Name)
|
||||
if err != nil {
|
||||
if !globalIsErasure && !globalIsDistErasure && errors.Is(err, errVolumeNotFound) {
|
||||
meta = newBucketMetadata(buckets[index].Name)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sys.Lock()
|
||||
sys.metadataMap[buckets[index].Name] = meta
|
||||
sys.Unlock()
|
||||
|
||||
globalEventNotifier.set(buckets[index], meta) // set notification targets
|
||||
|
||||
globalBucketTargetSys.set(buckets[index], meta) // set remote replication targets
|
||||
|
||||
return nil
|
||||
return sys.loadBucketMetadata(ctx, buckets[index])
|
||||
}, index)
|
||||
}
|
||||
for _, err := range g.Wait() {
|
||||
@@ -428,17 +433,51 @@ func (sys *BucketMetadataSys) concurrentLoad(ctx context.Context, buckets []Buck
|
||||
}
|
||||
}
|
||||
|
||||
func (sys *BucketMetadataSys) refreshBucketsMetadataLoop(ctx context.Context) {
|
||||
const bucketMetadataRefresh = 15 * time.Minute
|
||||
|
||||
t := time.NewTimer(bucketMetadataRefresh)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-t.C:
|
||||
buckets, err := sys.objAPI.ListBuckets(ctx, BucketOptions{})
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
continue
|
||||
}
|
||||
for i := range buckets {
|
||||
err := sys.loadBucketMetadata(ctx, buckets[i])
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
continue
|
||||
}
|
||||
// Check if there is a spare core, wait 100ms instead
|
||||
waitForLowIO(runtime.NumCPU(), 100*time.Millisecond, currentHTTPIO)
|
||||
}
|
||||
|
||||
t.Reset(bucketMetadataRefresh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loads bucket metadata for all buckets into BucketMetadataSys.
|
||||
func (sys *BucketMetadataSys) load(ctx context.Context, buckets []BucketInfo, objAPI ObjectLayer) {
|
||||
func (sys *BucketMetadataSys) init(ctx context.Context, buckets []BucketInfo) {
|
||||
count := 100 // load 100 bucket metadata at a time.
|
||||
for {
|
||||
if len(buckets) < count {
|
||||
sys.concurrentLoad(ctx, buckets, objAPI)
|
||||
return
|
||||
sys.concurrentLoad(ctx, buckets)
|
||||
break
|
||||
}
|
||||
sys.concurrentLoad(ctx, buckets[:count], objAPI)
|
||||
sys.concurrentLoad(ctx, buckets[:count])
|
||||
buckets = buckets[count:]
|
||||
}
|
||||
|
||||
if globalIsDistErasure {
|
||||
go sys.refreshBucketsMetadataLoop(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the state of the BucketMetadataSys.
|
||||
|
||||
@@ -75,6 +75,11 @@ func getConditionValues(r *http.Request, lc string, cred auth.Credentials) map[s
|
||||
groups = cred.Groups
|
||||
)
|
||||
|
||||
if cred.IsTemp() || cred.IsServiceAccount() {
|
||||
// For derived credentials, check the parent user's permissions.
|
||||
username = cred.ParentUser
|
||||
}
|
||||
|
||||
principalType := "Anonymous"
|
||||
if username != "" {
|
||||
principalType = "User"
|
||||
|
||||
@@ -411,6 +411,11 @@ func (er *erasureObjects) healObject(ctx context.Context, bucket string, object
|
||||
erasure.ShardFileSize(latestMeta.Parts[0].ActualSize) < smallFileThreshold)
|
||||
}
|
||||
|
||||
result.ObjectSize, err = latestMeta.GetActualSize()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Loop to find number of disks with valid data, per-drive
|
||||
// data state and a list of outdated disks on which data needs
|
||||
// to be healed.
|
||||
@@ -421,9 +426,6 @@ func (er *erasureObjects) healObject(ctx context.Context, bucket string, object
|
||||
switch {
|
||||
case v != nil:
|
||||
driveState = madmin.DriveStateOk
|
||||
// If data is sane on any one disk, we can
|
||||
// extract the correct object size.
|
||||
result.ObjectSize = partsMetadata[i].Size
|
||||
case errs[i] == errDiskNotFound, dataErrs[i] == errDiskNotFound:
|
||||
driveState = madmin.DriveStateOffline
|
||||
case errs[i] == errFileNotFound, errs[i] == errFileVersionNotFound, errs[i] == errVolumeNotFound:
|
||||
@@ -673,9 +675,6 @@ func (er *erasureObjects) healObject(ctx context.Context, bucket string, object
|
||||
})
|
||||
}
|
||||
|
||||
// Set the size of the object in the heal result
|
||||
result.ObjectSize = latestMeta.Size
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,15 +23,18 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/internal/amztime"
|
||||
"github.com/minio/minio/internal/bucket/replication"
|
||||
"github.com/minio/minio/internal/crypto"
|
||||
"github.com/minio/minio/internal/hash/sha256"
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/minio/internal/sync/errgroup"
|
||||
"github.com/minio/sio"
|
||||
)
|
||||
|
||||
// Object was stored with additional erasure codes due to degraded system at upload time
|
||||
@@ -104,6 +107,52 @@ func (fi FileInfo) IsValid() bool {
|
||||
correctIndexes)
|
||||
}
|
||||
|
||||
func (fi FileInfo) checkMultipart() (int64, bool) {
|
||||
if len(fi.Parts) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
if !crypto.IsMultiPart(fi.Metadata) {
|
||||
return 0, false
|
||||
}
|
||||
var size int64
|
||||
for _, part := range fi.Parts {
|
||||
psize, err := sio.DecryptedSize(uint64(part.Size))
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
size += int64(psize)
|
||||
}
|
||||
|
||||
return size, len(extractETag(fi.Metadata)) != 32
|
||||
}
|
||||
|
||||
// GetActualSize - returns the actual size of the stored object
|
||||
func (fi FileInfo) GetActualSize() (int64, error) {
|
||||
if _, ok := fi.Metadata[ReservedMetadataPrefix+"compression"]; ok {
|
||||
sizeStr, ok := fi.Metadata[ReservedMetadataPrefix+"actual-size"]
|
||||
if !ok {
|
||||
return -1, errInvalidDecompressedSize
|
||||
}
|
||||
size, err := strconv.ParseInt(sizeStr, 10, 64)
|
||||
if err != nil {
|
||||
return -1, errInvalidDecompressedSize
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
if _, ok := crypto.IsEncrypted(fi.Metadata); ok {
|
||||
size, ok := fi.checkMultipart()
|
||||
if !ok {
|
||||
size, err := sio.DecryptedSize(uint64(fi.Size))
|
||||
if err != nil {
|
||||
err = errObjectTampered // assign correct error type
|
||||
}
|
||||
return int64(size), err
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
return fi.Size, nil
|
||||
}
|
||||
|
||||
// ToObjectInfo - Converts metadata to object info.
|
||||
func (fi FileInfo) ToObjectInfo(bucket, object string, versioned bool) ObjectInfo {
|
||||
object = decodeDirObject(object)
|
||||
|
||||
@@ -1207,6 +1207,7 @@ func (z *erasureServerPools) ListObjectVersions(ctx context.Context, bucket, pre
|
||||
if marker == "" && versionMarker != "" {
|
||||
return loi, NotImplemented{}
|
||||
}
|
||||
|
||||
opts := listPathOptions{
|
||||
Bucket: bucket,
|
||||
Prefix: prefix,
|
||||
@@ -1217,6 +1218,17 @@ func (z *erasureServerPools) ListObjectVersions(ctx context.Context, bucket, pre
|
||||
AskDisks: globalAPIConfig.getListQuorum(),
|
||||
Versioned: true,
|
||||
}
|
||||
|
||||
// Shortcut for APN/1.0 Veeam/1.0 Backup/10.0
|
||||
// It requests unique blocks with a specific prefix.
|
||||
// We skip scanning the parent directory for
|
||||
// more objects matching the prefix.
|
||||
ri := logger.GetReqInfo(ctx)
|
||||
if ri != nil && strings.Contains(ri.UserAgent, `1.0 Veeam/1.0 Backup`) && strings.HasSuffix(prefix, ".blk") {
|
||||
opts.BaseDir = prefix
|
||||
opts.Transient = true
|
||||
}
|
||||
|
||||
// set bucket metadata in opts
|
||||
opts.setBucketMeta(ctx)
|
||||
|
||||
|
||||
@@ -96,7 +96,9 @@ func (z *erasureServerPools) listPath(ctx context.Context, o *listPathOptions) (
|
||||
|
||||
// Decode and get the optional list id from the marker.
|
||||
o.parseMarker()
|
||||
o.BaseDir = baseDirFromPrefix(o.Prefix)
|
||||
if o.BaseDir == "" {
|
||||
o.BaseDir = baseDirFromPrefix(o.Prefix)
|
||||
}
|
||||
o.Transient = o.Transient || isReservedOrInvalidBucket(o.Bucket, false)
|
||||
o.SetFilter()
|
||||
if o.Transient {
|
||||
|
||||
@@ -49,6 +49,7 @@ type listPathOptions struct {
|
||||
Bucket string
|
||||
|
||||
// Directory inside the bucket.
|
||||
// When unset listPath will set this based on Prefix
|
||||
BaseDir string
|
||||
|
||||
// Scan/return only content with prefix.
|
||||
|
||||
+1
-1
@@ -361,7 +361,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithSSO(w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
|
||||
// Validate JWT; check clientID in claims matches the one associated with the roleArn
|
||||
if err := globalOpenIDConfig.Validate(roleArn, token, accessToken, r.Form.Get(stsDurationSeconds), claims); err != nil {
|
||||
if err := globalOpenIDConfig.Validate(r.Context(), roleArn, token, accessToken, r.Form.Get(stsDurationSeconds), claims); err != nil {
|
||||
switch err {
|
||||
case openid.ErrTokenExpired:
|
||||
switch action {
|
||||
|
||||
@@ -38,6 +38,7 @@ func runAllIAMSTSTests(suite *TestSuiteIAM, c *check) {
|
||||
suite.TestSTS(c)
|
||||
suite.TestSTSWithDenyDeleteVersion(c)
|
||||
suite.TestSTSWithTags(c)
|
||||
suite.TestSTSServiceAccountsWithUsername(c)
|
||||
suite.TestSTSWithGroupPolicy(c)
|
||||
suite.TearDownSuite(c)
|
||||
}
|
||||
@@ -74,6 +75,99 @@ func TestIAMInternalIDPSTSServerSuite(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TestSuiteIAM) TestSTSServiceAccountsWithUsername(c *check) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
bucket := "dillon-bucket"
|
||||
err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
|
||||
if err != nil {
|
||||
c.Fatalf("bucket create error: %v", err)
|
||||
}
|
||||
|
||||
// Create policy
|
||||
policy := "mypolicy-username"
|
||||
policyBytes := []byte(`{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::${aws:username}-*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
|
||||
if err != nil {
|
||||
c.Fatalf("policy add error: %v", err)
|
||||
}
|
||||
|
||||
if err = s.adm.AddUser(ctx, "dillon", "dillon-123"); err != nil {
|
||||
c.Fatalf("policy add error: %v", err)
|
||||
}
|
||||
|
||||
err = s.adm.SetPolicy(ctx, policy, "dillon", false)
|
||||
if err != nil {
|
||||
c.Fatalf("Unable to set policy: %v", err)
|
||||
}
|
||||
|
||||
assumeRole := cr.STSAssumeRole{
|
||||
Client: s.TestSuiteCommon.client,
|
||||
STSEndpoint: s.endPoint,
|
||||
Options: cr.STSAssumeRoleOptions{
|
||||
AccessKey: "dillon",
|
||||
SecretKey: "dillon-123",
|
||||
Location: "",
|
||||
},
|
||||
}
|
||||
|
||||
value, err := assumeRole.Retrieve()
|
||||
if err != nil {
|
||||
c.Fatalf("Expected to generate STS creds, got err: %#v", err)
|
||||
}
|
||||
|
||||
// Check that the LDAP sts cred is actually working.
|
||||
minioClient, err := minio.New(s.endpoint, &minio.Options{
|
||||
Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
|
||||
Secure: s.secure,
|
||||
Transport: s.TestSuiteCommon.client.Transport,
|
||||
})
|
||||
if err != nil {
|
||||
c.Fatalf("Error initializing client: %v", err)
|
||||
}
|
||||
|
||||
// Validate that the client from sts creds can access the bucket.
|
||||
c.mustListObjects(ctx, minioClient, bucket)
|
||||
|
||||
// Create an madmin client with user creds
|
||||
userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
|
||||
Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
|
||||
Secure: s.secure,
|
||||
})
|
||||
if err != nil {
|
||||
c.Fatalf("Err creating user admin client: %v", err)
|
||||
}
|
||||
userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
|
||||
|
||||
// Create svc acc
|
||||
cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient)
|
||||
|
||||
svcClient := s.getUserClient(c, cr.AccessKey, cr.SecretKey, "")
|
||||
|
||||
// 1. Check S3 access for service account ListObjects()
|
||||
c.mustListObjects(ctx, svcClient, bucket)
|
||||
|
||||
// 2. Check S3 access for upload
|
||||
c.mustUpload(ctx, svcClient, bucket)
|
||||
|
||||
// 3. Check S3 access for download
|
||||
c.mustDownload(ctx, svcClient, bucket)
|
||||
}
|
||||
|
||||
func (s *TestSuiteIAM) TestSTSWithDenyDeleteVersion(c *check) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
@@ -326,4 +326,4 @@ minio server /data
|
||||
## Explore Further
|
||||
|
||||
* [MinIO Quickstart Guide](https://min.io/docs/minio/linux/index.html#quickstart-for-linux)
|
||||
* [Configure MinIO Server with TLS](https://min.io/docs/minio/linux/operations/network-encryption.htmls)
|
||||
* [Configure MinIO Server with TLS](https://min.io/docs/minio/linux/operations/network-encryption.html)
|
||||
|
||||
@@ -2,7 +2,7 @@ version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:RELEASE.2023-01-31T02-24-19Z
|
||||
image: quay.io/minio/minio:RELEASE.2023-02-09T05-16-53Z
|
||||
command: server --console-address ":9001" http://minio{1...4}/data{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
|
||||
@@ -70,39 +70,11 @@ func (opts Config) BitrotScanCycle() (d time.Duration) {
|
||||
return opts.cache.bitrotCycle
|
||||
}
|
||||
|
||||
// Wait waits for IOCount to go down or max sleep to elapse before returning.
|
||||
// usually used in healing paths to wait for specified amount of time to
|
||||
// throttle healing.
|
||||
func (opts Config) Wait(currentIO func() int, activeListeners func() int) {
|
||||
// Clone safely the heal configuration
|
||||
func (opts Config) Clone() (int, time.Duration, string) {
|
||||
configMutex.RLock()
|
||||
maxIO, maxWait := opts.IOCount, opts.Sleep
|
||||
configMutex.RUnlock()
|
||||
|
||||
// No need to wait run at full speed.
|
||||
if maxIO <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// At max 10 attempts to wait with 100 millisecond interval before proceeding
|
||||
waitTick := 100 * time.Millisecond
|
||||
|
||||
tmpMaxWait := maxWait
|
||||
|
||||
if currentIO != nil {
|
||||
for currentIO() >= maxIO+activeListeners() {
|
||||
if tmpMaxWait > 0 {
|
||||
if tmpMaxWait < waitTick {
|
||||
time.Sleep(tmpMaxWait)
|
||||
} else {
|
||||
time.Sleep(waitTick)
|
||||
}
|
||||
tmpMaxWait -= waitTick
|
||||
}
|
||||
if tmpMaxWait <= 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
defer configMutex.RUnlock()
|
||||
return opts.IOCount, opts.Sleep, opts.Bitrot
|
||||
}
|
||||
|
||||
// Update updates opts with nopts
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package openid
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -140,7 +141,7 @@ const (
|
||||
)
|
||||
|
||||
// Validate - validates the id_token.
|
||||
func (r *Config) Validate(arn arn.ARN, token, accessToken, dsecs string, claims jwtgo.MapClaims) error {
|
||||
func (r *Config) Validate(ctx context.Context, arn arn.ARN, token, accessToken, dsecs string, claims jwtgo.MapClaims) error {
|
||||
jp := new(jwtgo.Parser)
|
||||
jp.ValidMethods = []string{
|
||||
"RS256", "RS384", "RS512",
|
||||
@@ -184,7 +185,7 @@ func (r *Config) Validate(arn arn.ARN, token, accessToken, dsecs string, claims
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.updateUserinfoClaims(arn, accessToken, claims); err != nil {
|
||||
if err = r.updateUserinfoClaims(ctx, arn, accessToken, claims); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -223,7 +224,7 @@ func (r *Config) Validate(arn arn.ARN, token, accessToken, dsecs string, claims
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Config) updateUserinfoClaims(arn arn.ARN, accessToken string, claims map[string]interface{}) error {
|
||||
func (r *Config) updateUserinfoClaims(ctx context.Context, arn arn.ARN, accessToken string, claims map[string]interface{}) error {
|
||||
pCfg, ok := r.arnProviderCfgsMap[arn]
|
||||
// If claim user info is enabled, get claims from userInfo
|
||||
// and overwrite them with the claims from JWT.
|
||||
@@ -231,7 +232,7 @@ func (r *Config) updateUserinfoClaims(arn arn.ARN, accessToken string, claims ma
|
||||
if accessToken == "" {
|
||||
return errors.New("access_token is mandatory if user_info claim is enabled")
|
||||
}
|
||||
uclaims, err := pCfg.UserInfo(accessToken, r.transport)
|
||||
uclaims, err := pCfg.UserInfo(ctx, accessToken, r.transport)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package openid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -148,7 +149,7 @@ func TestJWTHMACType(t *testing.T) {
|
||||
}
|
||||
|
||||
var claims jwtgo.MapClaims
|
||||
if err = cfg.Validate(DummyRoleARN, token, "", "", claims); err != nil {
|
||||
if err = cfg.Validate(context.Background(), DummyRoleARN, token, "", "", claims); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -200,7 +201,7 @@ func TestJWT(t *testing.T) {
|
||||
}
|
||||
|
||||
var claims jwtgo.MapClaims
|
||||
if err = cfg.Validate(DummyRoleARN, u.Query().Get("Token"), "", "", claims); err == nil {
|
||||
if err = cfg.Validate(context.Background(), DummyRoleARN, u.Query().Get("Token"), "", "", claims); err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package openid
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -108,23 +109,25 @@ func (p *providerCfg) GetRoleArn() string {
|
||||
// claims as part of the normal oauth2 flow, instead rely
|
||||
// on service providers making calls to IDP to fetch additional
|
||||
// claims available from the UserInfo endpoint
|
||||
func (p *providerCfg) UserInfo(accessToken string, transport http.RoundTripper) (map[string]interface{}, error) {
|
||||
func (p *providerCfg) UserInfo(ctx context.Context, accessToken string, transport http.RoundTripper) (map[string]interface{}, error) {
|
||||
if p.JWKS.URL == nil || p.JWKS.URL.String() == "" {
|
||||
return nil, errors.New("openid not configured")
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, p.DiscoveryDoc.UserInfoEndpoint, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.DiscoveryDoc.UserInfoEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
if accessToken != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+accessToken)
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -140,10 +143,8 @@ func (p *providerCfg) UserInfo(accessToken string, transport http.RoundTripper)
|
||||
return nil, errors.New(resp.Status)
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
claims := map[string]interface{}{}
|
||||
|
||||
if err = dec.Decode(&claims); err != nil {
|
||||
if err = json.NewDecoder(resp.Body).Decode(&claims); err != nil {
|
||||
// uncomment this for debugging when needed.
|
||||
// reqBytes, _ := httputil.DumpRequest(req, false)
|
||||
// fmt.Println(string(reqBytes))
|
||||
|
||||
Reference in New Issue
Block a user