Skip to content

Commit

Permalink
fix(ui,api,cdsctl): create login verify command (#4887)
Browse files Browse the repository at this point in the history
  • Loading branch information
richardlt committed Jan 14, 2020
1 parent 9d6b3cc commit f6f93fd
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 50 deletions.
103 changes: 82 additions & 21 deletions cli/cdsctl/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
var loginCmd = cli.Command{
Name: "login",
Short: "Login to CDS",
Long: "For admin signup with LDAP driver, INIT_TOKEN environment variable must be set.",
Example: `Use it with 'eval' and 'env' flag to set environment variable: eval $(cds login -H API_URL -u USERNAME -p PASSWORD --env)`,
Flags: []cli.Flag{
{
Expand All @@ -47,11 +48,6 @@ var loginCmd = cli.Command{
Name: "password",
ShortHand: "p",
},
{
Name: "init-token",
Usage: "A CDS init token that can be used for first connection",
ShortHand: "i",
},
{
Name: "token",
Usage: "A CDS token that can be used to login with a builtin auth driver.",
Expand All @@ -60,7 +56,7 @@ var loginCmd = cli.Command{
}

func login() *cobra.Command {
return cli.NewCommand(loginCmd, loginRun, nil, cli.CommandWithoutExtraFlags)
return cli.NewCommand(loginCmd, loginRun, cli.SubCommands{loginVerify()}, cli.CommandWithoutExtraFlags)
}

func loginRun(v cli.Values) error {
Expand Down Expand Up @@ -116,15 +112,15 @@ func loginRun(v cli.Values) error {
if noInteractive {
return fmt.Errorf("Cannot signin with %s driver in no interactive mode", driverType)
}
req, err = loginRunExternal(v, driverType, apiURL)
return loginRunExternal(v, driverType, apiURL)
}
if err != nil {
return err
}

// For first connection ask for an optional init token
if drivers.IsFirstConnection {
initToken := v.GetString("init-token")
initToken := os.Getenv("INIT_TOKEN")
if initToken != "" {
req["init_token"] = initToken
}
Expand Down Expand Up @@ -196,36 +192,28 @@ func loginRunBuiltin(v cli.Values) (sdk.AuthConsumerSigninRequest, error) {
return req, nil
}

func loginRunExternal(v cli.Values, consumerType sdk.AuthConsumerType, apiURL string) (sdk.AuthConsumerSigninRequest, error) {
req := sdk.AuthConsumerSigninRequest{}

func loginRunExternal(v cli.Values, consumerType sdk.AuthConsumerType, apiURL string) error {
client := cdsclient.New(cdsclient.Config{
Host: apiURL,
Verbose: v.GetBool("verbose"),
})
config, err := client.ConfigUser()
if err != nil {
return req, err
return err
}

askSigninURI, err := url.Parse(config.URLUI + "/auth/ask-signin/" + string(consumerType) + "?origin=cdsctl")
if err != nil {
return req, fmt.Errorf("cannot parse given api uri: %v", err)
return fmt.Errorf("cannot parse given api uri: %v", err)
}

fmt.Println("cdsctl: Opening the browser to login or control-c to abort")
fmt.Println(" >\tWarning: If browser does not open, visit")
fmt.Println(" >\t" + cli.Green("%s", askSigninURI.String()))
browser.OpenURL(askSigninURI.String()) // nolint
fmt.Println(" >\tPlease follow instructions given on your browser to finish login.")

token := cli.AskPassword("Token")
splittedToken := strings.Split(token, ":")
if len(splittedToken) != 2 {
return req, fmt.Errorf("Invalid given token")
}
req["state"], req["code"] = splittedToken[0], splittedToken[1]

return req, nil
return nil
}

func doAfterLogin(client cdsclient.Interface, v cli.Values, apiURL string, driverType sdk.AuthConsumerType, res sdk.AuthConsumerSigninResponse) error {
Expand Down Expand Up @@ -424,3 +412,76 @@ func writeConfigFile(configFile string, content *bytes.Buffer) error {
}
return nil
}

func loginVerify() *cobra.Command {
return cli.NewCommand(loginVerifyCmd, loginVerifyFunc, nil, cli.CommandWithoutExtraFlags)
}

var loginVerifyCmd = cli.Command{
Name: "verify",
Long: "For admin signup INIT_TOKEN environment variable must be set.",
Hidden: true,
Args: []cli.Arg{
{
Name: "api-url",
AllowEmpty: false,
},
{
Name: "driver-type",
AllowEmpty: false,
},
{
Name: "token",
AllowEmpty: false,
},
},
}

func loginVerifyFunc(v cli.Values) error {
apiURL := v.GetString("api-url")

// Load all drivers from given CDS instance
client := cdsclient.New(cdsclient.Config{
Host: apiURL,
Verbose: os.Getenv("CDS_VERBOSE") == "true",
})
drivers, err := client.AuthDriverList()
if err != nil {
return fmt.Errorf("Cannot list auth drivers: %v", err)
}
if len(drivers.Drivers) == 0 {
return fmt.Errorf("No authentication driver configured")
}

driverType := sdk.AuthConsumerType(v.GetString("driver-type"))
if !driverType.IsValidExternal() {
return fmt.Errorf("Invalid given driver type: %s", driverType)
}

token := v.GetString("token")
splittedToken := strings.Split(token, ":")
if len(splittedToken) != 2 {
return fmt.Errorf("Invalid given token")
}

req := sdk.AuthConsumerSigninRequest{
"state": splittedToken[0],
"code": splittedToken[1],
}

// For first connection ask for an optional init token
if drivers.IsFirstConnection {
initToken := os.Getenv("INIT_TOKEN")
if initToken != "" {
req["init_token"] = initToken
}
}

// Send signin request
res, err := client.AuthConsumerSignin(driverType, req)
if err != nil {
return fmt.Errorf("cannot signin: %v", err)
}

return doAfterLogin(client, v, apiURL, driverType, res)
}
3 changes: 2 additions & 1 deletion cli/cdsctl/signup.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ func signupFunc(v cli.Values) error {

var signupVerifyCmd = cli.Command{
Name: "verify",
Short: "Verify local CDS signup.\nFor admin signup INIT_TOKEN environment variable must be set",
Short: "Verify local CDS signup.",
Long: "For admin signup INIT_TOKEN environment variable must be set.",
Args: []cli.Arg{
{
Name: "token",
Expand Down
2 changes: 1 addition & 1 deletion engine/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ func EnableTracing() HandlerConfigParam {

// NotFoundHandler is called by default by Mux is any matching handler has been found
func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
service.WriteError(w, req, sdk.ErrNotFound)
service.WriteError(w, req, sdk.WithStack(sdk.ErrNotFound))
}

// StatusPanic returns router status. If nbPanic > 30 -> Alert, if nbPanic > 0 -> Warn
Expand Down
5 changes: 0 additions & 5 deletions ui/src/app/model/config.model.ts

This file was deleted.

16 changes: 5 additions & 11 deletions ui/src/app/service/config/config.service.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import {HttpClient} from '@angular/common/http';
import {Injectable} from '@angular/core';
import {Config} from 'app/model/config.model';
import {Observable} from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

/**
* Service to get config
*/
@Injectable()
export class ConfigService {

constructor(private _http: HttpClient) {
}

/**
* Get the config (url api / url ui)
* @returns {Observable<Config>}
*/
getConfig(): Observable<Config> {
return this._http.get<Config>('/config/user');
getConfig(): Observable<any> {
return this._http.get<any>('/config/user');
}
}
14 changes: 10 additions & 4 deletions ui/src/app/views/auth/callback/callback.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { NgForm } from '@angular/forms';
import { ActivatedRoute, DefaultUrlSerializer, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { AuthenticationService } from 'app/service/authentication/authentication.service';
import { ConfigService } from 'app/service/services.module';
import { AutoUnsubscribe } from 'app/shared/decorator/autoUnsubscribe';
import { ToastService } from 'app/shared/toast/ToastService';
import { jws } from 'jsrsasign';
Expand All @@ -28,14 +29,16 @@ export class CallbackComponent implements OnInit {
showInitTokenForm: boolean;
consumerType: string;
payloadData: any;
cmd: string;

constructor(
private _route: ActivatedRoute,
private _cd: ChangeDetectorRef,
private _toast: ToastService,
private _translate: TranslateService,
private _router: Router,
private _authenticationService: AuthenticationService
private _authenticationService: AuthenticationService,
private _configService: ConfigService
) {
this.loading = true;
}
Expand All @@ -60,9 +63,12 @@ export class CallbackComponent implements OnInit {

// If the origin is cdsctl, show the code and the state for copy
if (this.payloadData && this.payloadData.origin === 'cdsctl') {
this.loading = false;
this.showCTL = true;
this._cd.markForCheck();
this._configService.getConfig().subscribe(config => {
this.cmd = `cdsctl login verify ${config['url.api']} ${this.consumerType} ${this.state}:${this.code}`
this.loading = false;
this.showCTL = true;
this._cd.markForCheck();
})
return;
}

Expand Down
11 changes: 6 additions & 5 deletions ui/src/app/views/auth/callback/callback.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
</div>
<form class="ui form">
<div class="field">
<label>Token</label>
<label>{{ 'auth_message_command' | translate }}</label>
<div class="ui fluid action input">
<input type="text" value="{{state + ':' + code}}" [readonly]="true">
<input type="text" value="{{cmd}}" [readonly]="true">
<button class="ui icon button copyButton" [title]="'common_copy_clipboard' | translate"
ngxClipboard [cbContent]="state + ':' + code" (click)="confirmCopy()">
ngxClipboard [cbContent]="cmd" (click)="confirmCopy()">
<i class="copy icon"></i>
</button>
</div>
Expand Down Expand Up @@ -50,7 +50,8 @@
</div>
<form class="ui form">
<div class="field">
<button class="fluid ui button primary" [class.loading]="loadingSignin" (click)="navigateToSignin()">
<button class="fluid ui button primary" [class.loading]="loadingSignin"
(click)="navigateToSignin()">
{{ 'account_btn_signin' | translate }}
</button>
</div>
Expand All @@ -59,4 +60,4 @@
</ng-template>
</div>
</div>
</div>
</div>
3 changes: 2 additions & 1 deletion ui/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"auth_button_signin_with": "Sign in with {{consumer}}",
"auth_value_copied": "Value copied",
"auth_message_init_token": "If you just generated the CDS configuration you can give an optional init token.",
"auth_message_copy": "You can now copy this parameters to your terminal then close this tab.",
"auth_message_command": "Command",
"auth_message_copy": "You can now copy this command to your terminal to finish login then close this tab.",
"auth_message_copy_signin_token": "You can now use this token to signin with the consumer.",
"auth_callback_missing_param": "One or more params are missing to continue. You can try to sign in again.",
"auth_callback_error": "A error occurred. You can try to sign in again.",
Expand Down
3 changes: 2 additions & 1 deletion ui/src/assets/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"auth_button_signin_with": "Se connecter avec {{consumer}}",
"auth_value_copied": "La valeur a été copiée",
"auth_message_init_token": "Si vous venez de générer la configuration de CDS, vous pouvez fournir la clé d'initialisation falculative.",
"auth_message_copy": "Vous pouvez maintenant copier ces paramètres dans votre terminal puis fermer cet onglet.",
"auth_message_command": "Commande",
"auth_message_copy": "Vous pouvez maintenant copier cette commande dans votre terminal pour terminer la connexion, puis fermer cet onglet.",
"auth_message_copy_signin_token": "Vous pouvez maintenant utiliser ce token pour vous identifier avec le client.",
"auth_callback_missing_param": "Un ou plusieurs paramètres sont manquants pour poursuivre. Vous pouvez essayer de vous connecter à nouveau.",
"auth_callback_error": "Une erreur est seurvenue. Vous pouvez essayer de vous connecter à nouveau.",
Expand Down

0 comments on commit f6f93fd

Please sign in to comment.