Skip to content

Commit

Permalink
fedramp: Add GovCloud configuration
Browse files Browse the repository at this point in the history
To allow users to login against the development FedRAMP GovCloud
environment, we add a way of tracking the various APIs and AWS Cognito
user pool and client data.
  • Loading branch information
vkareh committed Jun 8, 2022
1 parent 8e68200 commit 0c1dbdf
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 49 deletions.
123 changes: 80 additions & 43 deletions cmd/login/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ import (
"github.com/spf13/cobra"

"github.com/openshift/rosa/cmd/logout"
"github.com/openshift/rosa/pkg/fedramp"
"github.com/openshift/rosa/pkg/interactive"
"github.com/openshift/rosa/pkg/logging"
"github.com/openshift/rosa/pkg/ocm"
rprtr "github.com/openshift/rosa/pkg/reporter"
)

// #nosec G101
const uiTokenPage = "https://console.redhat.com/openshift/token/rosa"
var uiTokenPage string = "https://console.redhat.com/openshift/token/rosa"

var reAttempt bool

Expand All @@ -58,9 +59,8 @@ var Cmd = &cobra.Command{
"\t3. Environment variable (OCM_TOKEN)\n"+
"\t4. Configuration file\n"+
"\t5. Command-line prompt\n", uiTokenPage),
Example: " # Login to the OpenShift API with an existing token generated from " +
`https://console.redhat.com/openshift/token/rosa
rosa login --token=$OFFLINE_ACCESS_TOKEN`,
Example: fmt.Sprintf(` # Login to the OpenShift API with an existing token generated from %s
rosa login --token=$OFFLINE_ACCESS_TOKEN`, uiTokenPage),
Run: run,
}

Expand Down Expand Up @@ -110,7 +110,7 @@ func init() {
"token",
"t",
"",
"Access or refresh token generated from https://console.redhat.com/openshift/token/rosa.",
fmt.Sprintf("Access or refresh token generated from %s.", uiTokenPage),
)
flags.BoolVar(
&args.insecure,
Expand All @@ -119,14 +119,16 @@ func init() {
"Enables insecure communication with the server. This disables verification of TLS "+
"certificates and host names.",
)
fedramp.AddFlag(flags)
}

func run(cmd *cobra.Command, argv []string) {
reporter := rprtr.CreateReporterOrExit()
logger := logging.CreateLoggerOrExit(reporter)

// Check mandatory options:
if args.env == "" {
env := args.env
if env == "" {
reporter.Errorf("Option '--env' is mandatory")
os.Exit(1)
}
Expand All @@ -142,10 +144,23 @@ func run(cmd *cobra.Command, argv []string) {
}

token := args.token

if fedramp.Enabled() {
cfg.FedRAMP = true
} else if cfg.FedRAMP && token == "" {
fedramp.Enable()
cfg.FedRAMP = true
} else if fedramp.IsCognitoToken(token) {
fedramp.Enable()
cfg.FedRAMP = true
} else {
cfg.FedRAMP = false
}

haveReqs := token != ""

// Verify environment variables:
if !haveReqs && !reAttempt {
if !haveReqs && !reAttempt && !fedramp.Enabled() {
token = os.Getenv("ROSA_TOKEN")
if token == "" {
token = os.Getenv("OCM_TOKEN")
Expand All @@ -163,6 +178,10 @@ func run(cmd *cobra.Command, argv []string) {
haveReqs = armed
}

if fedramp.Enabled() {
uiTokenPage = fedramp.TokenPage[env]
}

// Prompt the user for token:
if !haveReqs {
fmt.Println("To login to your Red Hat account, get an offline access token at", uiTokenPage)
Expand All @@ -182,6 +201,41 @@ func run(cmd *cobra.Command, argv []string) {
os.Exit(1)
}

if token != "" {
if fedramp.Enabled() || fedramp.IsCognitoToken(token) {
cfg.AccessToken = ""
cfg.RefreshToken = token
cfg.FedRAMP = true
fedramp.Enable()
} else {
// If a token has been provided parse it:
parser := new(jwt.Parser)
jwtToken, _, err := parser.ParseUnverified(token, jwt.MapClaims{})
if err != nil {
reporter.Errorf("Failed to parse token '%s': %v", token, err)
os.Exit(1)
}

// Put the token in the place of the configuration that corresponds to its type:
typ, err := tokenType(jwtToken)
if err != nil {
reporter.Errorf("Failed to extract type from 'typ' claim of token '%s': %v", token, err)
os.Exit(1)
}
switch typ {
case "Bearer", "":
cfg.AccessToken = token
cfg.RefreshToken = ""
case "Refresh", "Offline":
cfg.AccessToken = ""
cfg.RefreshToken = token
default:
reporter.Errorf("Don't know how to handle token type '%s' in token '%s'", typ, token)
os.Exit(1)
}
}
}

// Apply the default OpenID details if not explicitly provided by the user:
tokenURL := sdk.DefaultTokenURL
if args.tokenURL != "" {
Expand All @@ -194,11 +248,26 @@ func run(cmd *cobra.Command, argv []string) {

// If the value of the `--env` is any of the aliases then replace it with the corresponding
// real URL:
gatewayURL, ok := ocm.URLAliases[args.env]
gatewayURL, ok := ocm.URLAliases[env]
if !ok {
gatewayURL = args.env
gatewayURL = env
}

if fedramp.Enabled() {
if env == sdk.DefaultURL {
env = "production"
}
if args.tokenURL == "" {
tokenURL = fedramp.TokenURLs[env]
}
if args.clientID == "" {
clientID = fedramp.ClientIDs[env]
}
gatewayURL, ok = fedramp.URLAliases[env]
if !ok {
gatewayURL = env
}
}
// Update the configuration with the values given in the command line:
cfg.TokenURL = tokenURL
cfg.ClientID = clientID
Expand All @@ -207,34 +276,6 @@ func run(cmd *cobra.Command, argv []string) {
cfg.URL = gatewayURL
cfg.Insecure = args.insecure

if token != "" {
// If a token has been provided parse it:
parser := new(jwt.Parser)
jwtToken, _, err := parser.ParseUnverified(token, jwt.MapClaims{})
if err != nil {
reporter.Errorf("Failed to parse token '%s': %v", token, err)
os.Exit(1)
}

// Put the token in the place of the configuration that corresponds to its type:
typ, err := tokenType(jwtToken)
if err != nil {
reporter.Errorf("Failed to extract type from 'typ' claim of token '%s': %v", token, err)
os.Exit(1)
}
switch typ {
case "Bearer", "":
cfg.AccessToken = token
cfg.RefreshToken = ""
case "Refresh", "Offline":
cfg.AccessToken = ""
cfg.RefreshToken = token
default:
reporter.Errorf("Don't know how to handle token type '%s' in token '%s'", typ, token)
os.Exit(1)
}
}

// Create a connection and get the token to verify that the crendentials are correct:
ocmClient, err := ocm.NewClient().
Config(cfg).
Expand Down Expand Up @@ -273,12 +314,8 @@ func run(cmd *cobra.Command, argv []string) {

username, err := cfg.GetData("username")
if err != nil {
reporter.Debugf("Failed to get username: %v", err)
username, err = cfg.GetData("preferred_username")
if err != nil {
reporter.Errorf("Failed to get username: %v", err)
os.Exit(1)
}
reporter.Errorf("Failed to get username: %v", err)
os.Exit(1)
}

reporter.Infof("Logged in as '%s' on '%s'", username, cfg.URL)
Expand Down
Loading

0 comments on commit 0c1dbdf

Please sign in to comment.