Skip to content

Commit 6703f31

Browse files
authored
Added session token based authentication
Added session token based authentication Added capability to use Session Token based authentication: NewSessionTokenSignatureProvider NewSessionTokenSignatureProviderFromFile Cloud only: added support to read region from system environment variable OCI_REGION if using user principal or session token authentication Minor code formatting cleanup from `go fmt`
1 parent bda05d2 commit 6703f31

24 files changed

+347
-51
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file.
33

44
The format is based on [Keep a Changelog](http://keepachangelog.com/).
55

6+
## Unreleased
7+
8+
### Added
9+
10+
- Added capability to use Session Token based authentication:
11+
* NewSessionTokenSignatureProvider
12+
* NewSessionTokenSignatureProviderFromFile
13+
- Cloud only: added support to read region from system environment variable
14+
OCI_REGION if using user principal or session token authentication
15+
616
## 1.4.1 - 2023-08-21
717

818
### Added

internal/test/cloudsim_config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
2-
"version": "21.2.19",
2+
"version": "22.4.10",
33
"tablePrefix": "Go",
44
"reCreateTables": true,
55
"verbose": true,
6+
"serialVersion": 0,
67
"dropTablesOnTearDown": true,
78
"clientConfig": {
89
"mode": "cloudsim",

nosqldb/auth/iam/configuration.go

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"errors"
88
"fmt"
99
"io/ioutil"
10+
"os"
1011
"regexp"
1112
"strings"
1213
"time"
@@ -21,11 +22,12 @@ type ConfigurationProvider interface {
2122
UserOCID() (string, error)
2223
KeyFingerprint() (string, error)
2324
Region() (string, error)
25+
SecurityTokenFile() (string, error)
2426
}
2527

2628
// IsConfigurationProviderValid Tests all parts of the configuration provider do not return an error
2729
func IsConfigurationProviderValid(conf ConfigurationProvider) (ok bool, err error) {
28-
baseFn := []func() (string, error){conf.TenancyOCID, conf.UserOCID, conf.KeyFingerprint, conf.KeyID}
30+
baseFn := []func() (string, error){conf.TenancyOCID, conf.KeyFingerprint, conf.KeyID}
2931
for _, fn := range baseFn {
3032
_, err = fn()
3133
ok = err == nil
@@ -39,6 +41,20 @@ func IsConfigurationProviderValid(conf ConfigurationProvider) (ok bool, err erro
3941
if err != nil {
4042
return
4143
}
44+
45+
// if using session token, user is not required
46+
sFile, err := conf.SecurityTokenFile()
47+
if err == nil && sFile != "" {
48+
return true, nil
49+
}
50+
51+
// otherwise user ocid is required
52+
_, err = conf.UserOCID()
53+
ok = err == nil
54+
if err != nil {
55+
return
56+
}
57+
4258
return true, nil
4359
}
4460

@@ -99,6 +115,10 @@ func (p rawConfigurationProvider) UserOCID() (string, error) {
99115
return p.user, nil
100116
}
101117

118+
func (p rawConfigurationProvider) SecurityTokenFile() (string, error) {
119+
return "", fmt.Errorf("RawConfigurationProvider does not support SecurityTokenFile")
120+
}
121+
102122
func (p rawConfigurationProvider) KeyFingerprint() (string, error) {
103123
if p.fingerprint == "" {
104124
return "", fmt.Errorf("fingerprint can not be empty")
@@ -150,8 +170,8 @@ func ConfigurationProviderFromFileWithProfile(configFilePath, profile, privateKe
150170
}
151171

152172
type configFileInfo struct {
153-
UserOcid, Fingerprint, KeyFilePath, TenancyOcid, Region, Passphrase string
154-
PresentConfiguration byte
173+
UserOcid, Fingerprint, KeyFilePath, TenancyOcid, Region, Passphrase, SecurityTokenFile string
174+
PresentConfiguration byte
155175
}
156176

157177
const (
@@ -161,6 +181,7 @@ const (
161181
hasRegion
162182
hasKeyFile
163183
hasPassphrase
184+
hasSecurityTokenFile
164185
none
165186
)
166187

@@ -216,6 +237,9 @@ func parseConfigAtLine(start int, content []string) (info *configFileInfo, err e
216237
case "tenancy":
217238
configurationPresent = configurationPresent | hasTenancy
218239
info.TenancyOcid = value
240+
case "security_token_file":
241+
configurationPresent = configurationPresent | hasSecurityTokenFile
242+
info.SecurityTokenFile = value
219243
case "region":
220244
configurationPresent = configurationPresent | hasRegion
221245
info.Region = value
@@ -241,6 +265,23 @@ func openConfigFile(configFilePath string) (data []byte, err error) {
241265
return
242266
}
243267

268+
func readTokenFromFile(tokenFilePath string) (string, error) {
269+
expandedPath, err := sdkutil.ExpandPath(tokenFilePath)
270+
if err != nil {
271+
err = fmt.Errorf("can not read token file: %s due to: %s", tokenFilePath, err.Error())
272+
return "", err
273+
}
274+
275+
data, err := ioutil.ReadFile(expandedPath)
276+
if err != nil {
277+
err = fmt.Errorf("can not read token file: %s due to: %s", tokenFilePath, err.Error())
278+
return "", err
279+
}
280+
281+
token := strings.Replace(string(data), "\r\n", "", -1)
282+
return strings.Replace(token, "\n", "", -1), nil
283+
}
284+
244285
func (p fileConfigurationProvider) String() string {
245286
return fmt.Sprintf("Configuration provided by file: %s", p.ConfigPath)
246287
}
@@ -286,6 +327,18 @@ func (p fileConfigurationProvider) TenancyOCID() (value string, err error) {
286327
return
287328
}
288329

330+
func (p fileConfigurationProvider) SecurityTokenFile() (value string, err error) {
331+
info, err := p.readAndParseConfigFile()
332+
if err != nil {
333+
err = fmt.Errorf("can not read security token configuration due to: %s", err.Error())
334+
return
335+
}
336+
337+
value, err = presentOrError(info.SecurityTokenFile, hasSecurityTokenFile,
338+
info.PresentConfiguration, "security_token_file")
339+
return
340+
}
341+
289342
func (p fileConfigurationProvider) UserOCID() (value string, err error) {
290343
info, err := p.readAndParseConfigFile()
291344
if err != nil {
@@ -312,11 +365,22 @@ func (p fileConfigurationProvider) ExpirationTime() time.Time {
312365
return time.Now().Add(24 * time.Hour)
313366
}
314367

315-
func (p fileConfigurationProvider) KeyID() (keyID string, err error) {
368+
func (p fileConfigurationProvider) KeyID() (string, error) {
316369
info, err := p.readAndParseConfigFile()
317370
if err != nil {
318371
err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
319-
return
372+
return "", err
373+
}
374+
375+
// SecurityTokenFile providers return different format for KeyID
376+
sFile, err := p.SecurityTokenFile()
377+
if sFile != "" && err == nil {
378+
// open/read file, return "ST$" + value (minus newlines)
379+
token, err := readTokenFromFile(sFile)
380+
if err != nil {
381+
return "", err
382+
}
383+
return "ST$" + token, nil
320384
}
321385

322386
return fmt.Sprintf("%s/%s/%s", info.TenancyOcid, info.UserOcid, info.Fingerprint), nil
@@ -365,7 +429,11 @@ func (p fileConfigurationProvider) Region() (value string, err error) {
365429

366430
value, err = presentOrError(info.Region, hasRegion, info.PresentConfiguration, "region")
367431
if err != nil {
368-
return
432+
// Attempt to read region from environment variable
433+
value = os.Getenv("OCI_REGION")
434+
if value == "" {
435+
return
436+
}
369437
}
370438

371439
return canStringBeRegion(value)
@@ -379,3 +447,24 @@ func canStringBeRegion(stringRegion string) (region string, err error) {
379447
}
380448
return stringRegion, nil
381449
}
450+
451+
func SessionTokenProviderFromFileWithProfile(configFilePath, profile, privateKeyPassword string) (ConfigurationProvider, error) {
452+
if profile == "" {
453+
profile = "DEFAULT"
454+
}
455+
provider, err := ConfigurationProviderFromFileWithProfile(configFilePath, profile, privateKeyPassword)
456+
if err != nil {
457+
return nil, err
458+
}
459+
// verify that the config specifies a security token file
460+
_, err = provider.SecurityTokenFile()
461+
if err != nil {
462+
return nil, err
463+
}
464+
// read the session token file, verify it has contents
465+
_, err = provider.KeyID()
466+
if err != nil {
467+
return nil, err
468+
}
469+
return provider, nil
470+
}

nosqldb/auth/iam/configuration_test.go

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ var (
1919
tkeyfile = "somelocation"
2020
ttenancy = "sometenancy"
2121
tregion = "someregion"
22+
tsessionfile = "somesessionfile"
2223
testPrivateKeyConf = `-----BEGIN RSA PRIVATE KEY-----
2324
MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF
2425
NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F
@@ -65,6 +66,14 @@ ufFtnLEj/5G9a8A//MFrXsXePUeBDEzjtEcjPGNxe0ZkuOgYx11Zc0R4oLI7LoHO
6566
07vtw4qCH4hztCJ5+JOUac6sGcILFRc4vSQQ15Cg5QEdBiSbQ/yo1P0hbNtSvnwO
6667
-----END RSA PRIVATE KEY-----`
6768
testKeyPassphrase = "goisfun"
69+
testSecurityToken = `bKbv8X2oyfxwp55w3MVKj1bfWnhvQgyqJ/1dER53STao3qRS26epRoBc0BoLtrNj
70+
L+Wfa3NeuEinetDYKRwWGHZqvbs/3PD5OKIXW1y/EAlg1vr6JWX8KxhQ0PzGJOdQ
71+
KPcB2duDtlNJ4awoGEsSp/qYyJLKOKpcz893OWTe3Oi9aQpzuL+kgH6VboCUdwdl
72+
Ub7YyTMFBkGzzjOXV/iSJDaxvVUIZt7CQS/DkBq4IHXX8iFUDzh6L297/BuRp3Q8
73+
9ydxmHQjNNtyH+RceZFn07IkWvPveo2BXpK4K9DXE39Z/g1nQzwTqgN8diXxwRuN
74+
Ge97lBWup4vP1TV8nyHW2AppgFVuPynO+XWfZUuCUzxNseB+XOyeqitoM4uvSNax
75+
DQmokjIf4qXC/46EnJ/fd9Ydz4GVQ4TYyxwNCBJK39RdUOcUtyI+A3IbZ+vt2HIV`
76+
testSessionKeyID = "ST$bKbv8X2oyfxwp55w3MVKj1bfWnhvQgyqJ/1dER53STao3qRS26epRoBc0BoLtrNjL+Wfa3NeuEinetDYKRwWGHZqvbs/3PD5OKIXW1y/EAlg1vr6JWX8KxhQ0PzGJOdQKPcB2duDtlNJ4awoGEsSp/qYyJLKOKpcz893OWTe3Oi9aQpzuL+kgH6VboCUdwdlUb7YyTMFBkGzzjOXV/iSJDaxvVUIZt7CQS/DkBq4IHXX8iFUDzh6L297/BuRp3Q89ydxmHQjNNtyH+RceZFn07IkWvPveo2BXpK4K9DXE39Z/g1nQzwTqgN8diXxwRuNGe97lBWup4vP1TV8nyHW2AppgFVuPynO+XWfZUuCUzxNseB+XOyeqitoM4uvSNaxDQmokjIf4qXC/46EnJ/fd9Ydz4GVQ4TYyxwNCBJK39RdUOcUtyI+A3IbZ+vt2HIV"
6877
)
6978

7079
func removeFileFn(filename string) {
@@ -86,15 +95,17 @@ key_file=somelocation
8695
tenancy=sometenancy
8796
compartment = somecompartment
8897
region=someregion
98+
security_token_file=somesessionfile
8999
`
90100
c, e := parseConfigFile([]byte(data), "DEFAULT")
91101

92102
assert.NoError(t, e)
93-
assert.Equal(t, c.UserOcid, tuser)
94-
assert.Equal(t, c.Fingerprint, tfingerprint)
95-
assert.Equal(t, c.KeyFilePath, tkeyfile)
96-
assert.Equal(t, c.TenancyOcid, ttenancy)
97-
assert.Equal(t, c.Region, tregion)
103+
assert.Equal(t, tuser, c.UserOcid)
104+
assert.Equal(t, tfingerprint, c.Fingerprint)
105+
assert.Equal(t, tkeyfile, c.KeyFilePath)
106+
assert.Equal(t, ttenancy, c.TenancyOcid)
107+
assert.Equal(t, tregion, c.Region)
108+
assert.Equal(t, tsessionfile, c.SecurityTokenFile)
98109
}
99110

100111
func TestFileConfigurationProvider_ParseEmptyFile(t *testing.T) {
@@ -303,6 +314,108 @@ region=someregion
303314
assert.NotEmpty(t, keyID)
304315
}
305316

317+
318+
func TestSessionConfigurationProvider_FromFileFn(t *testing.T) {
319+
dataTpl := `[DEFAULT]
320+
user=someuser
321+
fingerprint=somefingerprint
322+
key_file=%s
323+
security_token_file=%s
324+
tenancy=sometenancy
325+
region=someregion
326+
`
327+
328+
keyFile := writeTempFile(testPrivateKeyConf)
329+
tokenFile := writeTempFile(testSecurityToken)
330+
data := fmt.Sprintf(dataTpl, keyFile, tokenFile)
331+
tmpConfFile := writeTempFile(data)
332+
333+
defer removeFileFn(tmpConfFile)
334+
defer removeFileFn(keyFile)
335+
defer removeFileFn(tokenFile)
336+
337+
c, e0 := SessionTokenProviderFromFileWithProfile(tmpConfFile, "", "")
338+
assert.NoError(t, e0)
339+
assert.NotNil(t, c)
340+
rskey, e := c.PrivateRSAKey()
341+
keyID, e1 := c.KeyID()
342+
tFile, e2 := c.SecurityTokenFile()
343+
assert.NoError(t, e)
344+
assert.NotEmpty(t, rskey)
345+
assert.NoError(t, e1)
346+
assert.Equal(t, testSessionKeyID, keyID)
347+
assert.NoError(t, e2)
348+
assert.Equal(t, tokenFile, tFile)
349+
}
350+
351+
func TestSessionTokenSignatureProvider_FromFileFn(t *testing.T) {
352+
dataTpl := `[DEFAULT]
353+
user=someuser
354+
fingerprint=somefingerprint
355+
key_file=%s
356+
security_token_file=%s
357+
tenancy=sometenancy
358+
region=someregion
359+
`
360+
361+
keyFile := writeTempFile(testPrivateKeyConf)
362+
tokenFile := writeTempFile(testSecurityToken)
363+
data := fmt.Sprintf(dataTpl, keyFile, tokenFile)
364+
tmpConfFile := writeTempFile(data)
365+
366+
defer removeFileFn(tmpConfFile)
367+
defer removeFileFn(keyFile)
368+
defer removeFileFn(tokenFile)
369+
370+
c, e0 := NewSessionTokenSignatureProviderFromFile(tmpConfFile, "", "")
371+
assert.NoError(t, e0)
372+
assert.NotNil(t, c)
373+
}
374+
375+
func TestSessionConfigurationProvider_MissingToken(t *testing.T) {
376+
// note missing "security_token_file" field
377+
dataTpl := `[DEFAULT]
378+
user=someuser
379+
fingerprint=somefingerprint
380+
key_file=%s
381+
tenancy=sometenancy
382+
region=someregion
383+
`
384+
385+
keyFile := writeTempFile(testPrivateKeyConf)
386+
data := fmt.Sprintf(dataTpl, keyFile)
387+
tmpConfFile := writeTempFile(data)
388+
389+
defer removeFileFn(tmpConfFile)
390+
defer removeFileFn(keyFile)
391+
392+
c, e0 := SessionTokenProviderFromFileWithProfile(tmpConfFile, "", "")
393+
assert.Error(t, e0)
394+
assert.Nil(t, c)
395+
}
396+
397+
func TestSessionConfigurationProvider_BadTokenFile(t *testing.T) {
398+
dataTpl := `[DEFAULT]
399+
user=someuser
400+
fingerprint=somefingerprint
401+
key_file=%s
402+
security_token_file=gibberish
403+
tenancy=sometenancy
404+
region=someregion
405+
`
406+
407+
keyFile := writeTempFile(testPrivateKeyConf)
408+
data := fmt.Sprintf(dataTpl, keyFile)
409+
tmpConfFile := writeTempFile(data)
410+
411+
defer removeFileFn(tmpConfFile)
412+
defer removeFileFn(keyFile)
413+
414+
c, e0 := SessionTokenProviderFromFileWithProfile(tmpConfFile, "", "")
415+
assert.Error(t, e0)
416+
assert.Nil(t, c)
417+
}
418+
306419
func TestFileConfigurationProvider_FromFileAndProfile(t *testing.T) {
307420
dataTpl := `[DEFAULT]
308421
user=someuser

nosqldb/auth/iam/federation_client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ func newInstancePrincipalToken(tokenString string) (newToken securityToken, err
655655
if jwtToken, err = parseJwt(tokenString); err != nil {
656656
return nil, fmt.Errorf("failed to parse the token string \"%s\": %s", tokenString, err.Error())
657657
}
658-
newToken = &instancePrincipalToken{tokenString, jwtToken};
658+
newToken = &instancePrincipalToken{tokenString, jwtToken}
659659
if !newToken.Valid() {
660660
return nil, fmt.Errorf("Expired or invalid token string \"%s\"", tokenString)
661661
}

0 commit comments

Comments
 (0)