//go:build full

package main

import (
	"context"

	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
	azidentity "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
	a "github.com/microsoft/kiota-authentication-azure-go"
	msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
	log "github.com/sirupsen/logrus"
	"github.com/tg123/sshpiper/libplugin"
	"github.com/urfave/cli/v2"
)

func main() {
	libplugin.CreateAndRunPluginTemplate(&libplugin.PluginTemplate{
		Name:  "azdevicecode",
		Usage: "sshpiperd azure devicecode plugin, use devicecode to before ssh, see https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code",
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:     "tenant-id",
				Usage:    "Azure AD tenant id",
				EnvVars:  []string{"SSHPIPERD_AZDEVICECODE_TENANT_ID"},
				Required: true,
			},
			&cli.StringFlag{
				Name:     "client-id",
				Usage:    "Azure AD client id",
				EnvVars:  []string{"SSHPIPERD_AZDEVICECODE_CLIENT_ID"},
				Required: true,
			},
			&cli.BoolFlag{
				Name:    "no-read-graph",
				Usage:   "disable query user info from user graph",
				EnvVars: []string{"SSHPIPERD_AZDEVICECODE_NOREADGRAPH"},
			},
			&cli.StringFlag{
				Name:    "scope",
				Usage:   "permission scope when querying user info",
				EnvVars: []string{"SSHPIPERD_AZDEVICECODE_SCOPE"},
				Value:   "User.Read",
			},
		},
		CreateConfig: func(c *cli.Context) (*libplugin.SshPiperPluginConfig, error) {

			return &libplugin.SshPiperPluginConfig{
				KeyboardInteractiveCallback: func(conn libplugin.ConnMetadata, client libplugin.KeyboardInteractiveChallenge) (*libplugin.Upstream, error) {

					cred, err := azidentity.NewDeviceCodeCredential(&azidentity.DeviceCodeCredentialOptions{
						TenantID: c.String("tenant-id"),
						ClientID: c.String("client-id"),
						UserPrompt: func(ctx context.Context, message azidentity.DeviceCodeMessage) error {
							_, err := client("", message.Message, "", false)
							return err
						},
					})

					if err != nil {
						return nil, err
					}

					if c.Bool("no-read-graph") {
						_, err = cred.GetToken(context.Background(), policy.TokenRequestOptions{
							Scopes: []string{c.String("scope")},
						})

						return nil, err
					}

					auth, err := a.NewAzureIdentityAuthenticationProviderWithScopes(cred, []string{c.String("scope")})
					if err != nil {
						return nil, err
					}

					adapter, err := msgraphsdk.NewGraphRequestAdapter(auth)
					if err != nil {
						return nil, err
					}

					gsclient := msgraphsdk.NewGraphServiceClient(adapter)
					result, err := gsclient.Me().Get(context.Background(), nil)
					if err != nil {
						return nil, err
					}

					userId := *result.GetId()

					log.Infof("success with challenged username: %s", userId)

					return &libplugin.Upstream{
						Auth: libplugin.CreateNextPluginAuth(map[string]string{
							"UserId": *result.GetId(),
						}),
					}, nil
				},
			}, nil
		},
	})
}
