Skip to content

Commit

Permalink
add auth from CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
cyrildiagne committed Jan 4, 2020
1 parent 46bf86d commit bcfe32f
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 47 deletions.
80 changes: 80 additions & 0 deletions cmd/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package cmd

import (
"context"
"encoding/json"
"errors"
"net/http"
"os"
"os/exec"
"time"

"github.com/cyrildiagne/kuda/pkg/auth"

"github.com/gorilla/mux"
)

var authRedirectServer *http.Server
var user *auth.User

func enableCORS(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
}

func handleToken(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
decoder := json.NewDecoder(r.Body)
user = &auth.User{}
err := decoder.Decode(user)
if err != nil {
panic(err)
}

go func() {
time.Sleep(1 * time.Second)
// Shutdown server
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := authRedirectServer.Shutdown(ctx); err != nil {
panic(err)
}
}()
}

func startServer(port string) (*auth.User, error) {
r := mux.NewRouter()
r.HandleFunc("/", handleToken).Methods("POST")
r.HandleFunc("/", enableCORS).Methods("OPTIONS")

authRedirectServer = &http.Server{Addr: ":" + port, Handler: r}

if err := authRedirectServer.ListenAndServe(); err != nil {
if user != nil {
return user, nil
}
return nil, err
}
return nil, errors.New("could not retrieve user auth")
}

func startLoginFlow(authURL string) (*auth.User, error) {
port := os.Getenv("KUDA_CLI_LOGIN_PORT")
if port == "" {
port = "8094"
}
// Append redirect command.
authURL += "?cli=" + port
// Run command.
args := []string{authURL}
cmd := exec.Command("open", args...)
if err := cmd.Run(); err != nil {
return nil, err
}
user, err := startServer(port)
if err != nil {
return nil, err
}
return user, nil
}
4 changes: 2 additions & 2 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func deployWithRemote(manifest *latest.Manifest, dryRun bool) error {
// Defer the deletion of the temp tar file.
defer os.Remove(output.Name())

fmt.Println("Sending to deployer:", cfg.Deployer.Remote.URL)
fmt.Println("Sending to deployer:", cfg.Deployer.Remote.DeployerURL)

// Create request
body := &bytes.Buffer{}
Expand All @@ -104,7 +104,7 @@ func deployWithRemote(manifest *latest.Manifest, dryRun bool) error {
// Close writer
writer.Close()

url := cfg.Deployer.Remote.URL
url := cfg.Deployer.Remote.DeployerURL
req, err := http.NewRequest("POST", url, body)
req.Header.Set("Content-Type", writer.FormDataContentType())

Expand Down
27 changes: 25 additions & 2 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,31 @@ var initCmd = &cobra.Command{

} else {

// Setup the skaffold config.
// Setup the default remote config.
authURL := "https://auth.kuda." + deployer
authURLFlag, _ := cmd.Flags().GetString("auth_url")
if authURLFlag != "" {
authURL = authURLFlag
}
deployerURL := "https://deployer.kuda." + deployer
deployerURLFlag, _ := cmd.Flags().GetString("deployer_url")
if deployerURLFlag != "" {
deployerURL = deployerURLFlag
}
newCfg.Deployer.Remote = &config.RemoteDeployerConfig{
URL: deployer,
AuthURL: authURL,
DeployerURL: deployerURL,
}

// Start login flow.
fmt.Println("Authenticating on...", newCfg.Deployer.Remote.AuthURL)
user, err := startLoginFlow(newCfg.Deployer.Remote.AuthURL)
if err != nil {
fmt.Println("Authentication error.")
panic(err)
}
newCfg.Deployer.Remote.User = user
fmt.Println("Authenticated as", user.DisplayName)

// Write the file to disk.
writeConfig(newCfg)
Expand All @@ -62,6 +83,8 @@ func init() {

initCmd.Flags().StringP("namespace", "n", "default", "Knative namespace.")
initCmd.Flags().StringP("docker_registry", "d", "", "Docker registry.")
initCmd.Flags().String("auth_url", "", "Authentication URL.")
initCmd.Flags().String("deployer_url", "", "Deployer URL.")
}

func writeConfig(cfg config.UserConfig) error {
Expand Down
16 changes: 11 additions & 5 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Initializes the local configuration.

- `[-n, --namespace]` Your namespace. (default: `default`)
- `[-d, --docker-registry]` Required when using the `skaffold` deployer.
- `[--auth-url]` Specify which url to use for authentication when using a remote deployer.
- `[--deployer-url]` Specify which url to use for deployment when using a remote deployer.

**Examples**

Expand All @@ -24,18 +26,22 @@ kuda init -d gcr.io/my-gcp-project skaffold
```

<!--
```bash
kuda init \
-n your-namespace \
gpu.sh
```
```bash
kuda init \
-n your-namespace
-d gcr.io/my-gcp-project \
--auth_url localhost:8070 \
--deployer_url localhost:8090
localhost:8080
```
```bash
kuda init \
-n your-namespace \
deploy.kuda.gpu.sh
``` -->
-->

## → Dev

Expand Down
6 changes: 3 additions & 3 deletions docs/install_cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The best way to get started quickly on a cost-effective, fully managed cluster.
```bash
kuda init \
-n $your_namespace \
deploy.kuda.gpu.sh
gpu.sh
```
Replace `$your_namespace` with your [gpu.sh](#) username. -->
Expand All @@ -43,11 +43,11 @@ kuda init \
```

<!-- ```bash
If you've installed and configured a [Kuda Deployer](#):
If you've installed and configured a [Kuda Deployer](#):
kuda init \
-n $your_namespace \
deploy.kuda.$your_domain
$your_domain
```
-->

84 changes: 50 additions & 34 deletions images/auth/public/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
let uiContainer;
let currentUser;

const cli = new URL(window.location.href).searchParams.get("cli");

// Setup Firebase UI.
const uiConfig = {
Expand All @@ -15,20 +16,33 @@ const uiConfig = {
privacyPolicyUrl: config.privacyPolicyURL
};

function handleAuthChanged(user) {
async function handleAuthChanged(user) {
if (user) {
currentUser = user;
uiContainer.style.display = "none";
user.getIdToken().then(accessToken => {
accessToken = await user.getIdToken();
const userData = JSON.stringify(user, null, " ");
// Retrieve CLI argument.
if (cli) {
const url = "http://localhost:" + cli;
try {
await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: userData
});
} catch (e) {
document.getElementById("account-details").textContent =
"Error: Authentication successful but could not reach CLI.";
console.error(e);
return;
}
const msg = "Authentication successful! You can now close this window.";
document.getElementById("account-details").textContent = msg;
} else {
document.getElementById("sign-out").style.display = "inline";
document.getElementById("account-details").textContent = JSON.stringify(
currentUser,
null,
" "
);
});
document.getElementById("account-details").textContent = userData;
}
} else {
currentUser = null;
// User is signed out.
document.getElementById("sign-out").style.display = "none";
document.getElementById("account-details").textContent = "";
Expand All @@ -37,30 +51,32 @@ function handleAuthChanged(user) {
}
}

window.onload = () => {
uiContainer = document.getElementById("firebaseui-auth-container");
// window.onload = () => {
uiContainer = document.getElementById("firebaseui-auth-container");

firebase.initializeApp({
apiKey: config.apiKey,
authDomain: config.authDomain
});
firebase.initializeApp({
apiKey: config.apiKey,
authDomain: config.authDomain
});

// Listen to change in auth state so it displays the correct UI for when
// the user is signed in or not.
firebase.auth().onAuthStateChanged(handleAuthChanged);
// Listen to change in auth state so it displays the correct UI for when
// the user is signed in or not.
firebase.auth().onAuthStateChanged(handleAuthChanged);

// Signout
const signOutButton = document.getElementById("sign-out");
signOutButton.addEventListener("click", () => {
firebase
.auth()
.signOut()
.then(res => {
ui.start("#firebaseui-auth-container", uiConfig);
});
});
// Signout
const signOutButton = document.getElementById("sign-out");
signOutButton.addEventListener("click", () => {
firebase
.auth()
.signOut()
.then(res => {
ui.start("#firebaseui-auth-container", uiConfig);
});
});

// Start Firebase UI.
ui = new firebaseui.auth.AuthUI(firebase.auth());
ui.start("#firebaseui-auth-container", uiConfig);
};
// Start Firebase UI.
ui = new firebaseui.auth.AuthUI(firebase.auth());
ui.start("#firebaseui-auth-container", uiConfig);
if (cli) {
ui.disableAutoSignIn();
}
17 changes: 17 additions & 0 deletions pkg/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package auth

// User represents the User object.
type User struct {
UID string `json:"uid,omitempty"`
DisplayName string `json:"displayName,omitempty" yaml:"displayName,omitempty"`
Email string `json:"email,omitempty"`
Token Token `json:"stsTokenManager,omitempty"`
}

// Token stores the Oauth2 Token.
type Token struct {
APIKey string `json:"apiKey,omitempty" yaml:"apiKey,omitempty"`
RefreshToken string `json:"refreshToken,omitempty" yaml:"refreshToken,omitempty"`
AccessToken string `json:"accessToken,omitempty" yaml:"accessToken,omitempty"`
ExpirationTime int `json:"expirationTime,omitempty" yaml:"expirationTime,omitempty"`
}
5 changes: 4 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
v1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1"
"github.com/cyrildiagne/kuda/pkg/auth"
)

// UserConfig stores a local user configuration.
Expand All @@ -26,7 +27,9 @@ type SkaffoldDeployerConfig struct {

// RemoteDeployerConfig stores a remote deployer config.
type RemoteDeployerConfig struct {
URL string `yaml:"url"`
AuthURL string `yaml:"auth"`
DeployerURL string `yaml:"deployer"`
User *auth.User `yaml:"user"`
}

// Helpers
Expand Down

0 comments on commit bcfe32f

Please sign in to comment.