github.com/yaegashi/msgraph.go@v0.1.4/cmd/msgraph-sshpubkey/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"flag"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"log"
     9  	"net/http"
    10  	"os"
    11  	"strings"
    12  
    13  	"github.com/yaegashi/msgraph.go/jsonx"
    14  	"github.com/yaegashi/msgraph.go/msauth"
    15  	msgraph "github.com/yaegashi/msgraph.go/v1.0"
    16  	"golang.org/x/oauth2"
    17  )
    18  
    19  const (
    20  	defaultExtensionName = "dev.l0w.ssh_public_keys"
    21  	defaultTenantID      = "common"
    22  	defaultClientID      = "45c7f99c-0a94-42ff-a6d8-a8d657229e8c"
    23  )
    24  
    25  var defaultScopes = []string{"openid", "User.ReadWrite"}
    26  
    27  // Config is serializable configuration
    28  type Config struct {
    29  	TenantID      string            `json:"tenant_id,omitempty"`
    30  	ClientID      string            `json:"client_id,omitempty"`
    31  	ClientSecret  string            `json:"client_secret,omitempty"`
    32  	ExtensionName string            `json:"extension_name,omitempty"`
    33  	TokenCache    string            `json:"token_cache,omitempty"`
    34  	LoginMap      map[string]string `json:"login_map"`
    35  }
    36  
    37  // App is application
    38  type App struct {
    39  	Config
    40  	Op          string
    41  	In          string
    42  	Out         string
    43  	Login       string
    44  	TokenSource oauth2.TokenSource
    45  	GraphClient *msgraph.GraphServiceRequestBuilder
    46  }
    47  
    48  // User returns target graph user endpoint
    49  func (app *App) User() *msgraph.UserRequestBuilder {
    50  	if app.Login == "" {
    51  		return app.GraphClient.Me()
    52  	}
    53  	userID := app.Login
    54  	if app.LoginMap != nil {
    55  		if id, ok := app.LoginMap[app.Login]; ok {
    56  			userID = id
    57  		}
    58  	}
    59  	return app.GraphClient.Users().ID(userID)
    60  }
    61  
    62  // Authenticate performs OAuth2 authentication
    63  func (app *App) Authenticate(ctx context.Context) error {
    64  	var err error
    65  	m := msauth.NewManager()
    66  	if app.ClientSecret == "" {
    67  		if app.TokenCache != "" {
    68  			// ignore errors
    69  			m.LoadFile(app.TokenCache)
    70  		}
    71  		app.TokenSource, err = m.DeviceAuthorizationGrant(ctx, app.TenantID, app.ClientID, defaultScopes, nil)
    72  		if err != nil {
    73  			return err
    74  		}
    75  		if app.TokenCache != "" {
    76  			err = m.SaveFile(app.TokenCache)
    77  			if err != nil {
    78  				return err
    79  			}
    80  		}
    81  	} else {
    82  		scopes := []string{msauth.DefaultMSGraphScope}
    83  		app.TokenSource, err = m.ClientCredentialsGrant(ctx, app.TenantID, app.ClientID, app.ClientSecret, scopes)
    84  		if err != nil {
    85  			return err
    86  		}
    87  	}
    88  	app.GraphClient = msgraph.NewClient(oauth2.NewClient(ctx, app.TokenSource))
    89  	return nil
    90  }
    91  
    92  // Set performs set operation on user's extension
    93  func (app *App) Set(ctx context.Context) error {
    94  	err := app.Authenticate(ctx)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	var in []byte
    99  	if app.In == "-" {
   100  		in, err = ioutil.ReadAll(os.Stdin)
   101  	} else {
   102  		in, err = ioutil.ReadFile(app.In)
   103  	}
   104  	if err != nil {
   105  		return err
   106  	}
   107  	newExt := &msgraph.Extension{}
   108  	newExt.SetAdditionalData("extensionName", app.ExtensionName)
   109  	newExt.SetAdditionalData("value", string(in))
   110  	_, err = app.User().Extensions().Request().Add(ctx, newExt)
   111  	if err != nil {
   112  		if errRes, ok := err.(*msgraph.ErrorResponse); ok {
   113  			if errRes.StatusCode() == http.StatusConflict {
   114  				err = app.User().Extensions().ID(app.ExtensionName).Request().Update(ctx, newExt)
   115  			}
   116  		}
   117  	}
   118  	return err
   119  }
   120  
   121  // Get performs get operation on user's extensions
   122  func (app *App) Get(ctx context.Context) error {
   123  	err := app.Authenticate(ctx)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	r := app.User().Request()
   128  	r.Select("id")
   129  	r.Expand("extensions")
   130  	user, err := r.Get(ctx)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	for _, x := range user.Extensions {
   135  		if *x.ID != app.ExtensionName {
   136  			continue
   137  		}
   138  		value, _ := x.GetAdditionalData("value")
   139  		if s, ok := value.(string); ok {
   140  			if app.Out == "-" {
   141  				fmt.Print(s)
   142  				return nil
   143  			}
   144  			return ioutil.WriteFile(app.Out, []byte(s), 0644)
   145  		}
   146  		return fmt.Errorf("No value in extension %s", app.ExtensionName)
   147  	}
   148  	return nil
   149  }
   150  
   151  // Delete performs delete operation on user's extension
   152  func (app *App) Delete(ctx context.Context) error {
   153  	err := app.Authenticate(ctx)
   154  	if err != nil {
   155  		return err
   156  	}
   157  	return app.User().Extensions().ID(app.ExtensionName).Request().Delete(ctx)
   158  }
   159  
   160  func main() {
   161  	config := ""
   162  	app := &App{}
   163  	flag.StringVar(&config, "config", "", "Config file path")
   164  	flag.StringVar(&app.Op, "op", "GET", "Operation (GET/SET/DELETE)")
   165  	flag.StringVar(&app.TenantID, "tenant-id", "", "Tenant ID (default:"+defaultTenantID+")")
   166  	flag.StringVar(&app.ClientID, "client-id", "", "Client ID (default: "+defaultClientID+")")
   167  	flag.StringVar(&app.ClientSecret, "client-secret", "", "Client secret (for client credentials grant)")
   168  	flag.StringVar(&app.TokenCache, "token-cache", "", "OAuth2 token cache path")
   169  	flag.StringVar(&app.ExtensionName, "extension-name", "", "Extension name (default: "+defaultExtensionName+")")
   170  	flag.StringVar(&app.Login, "login", "", "Login name (default: authenticated user)")
   171  	flag.StringVar(&app.In, "in", "", "Input file (\"-\" for stdin)")
   172  	flag.StringVar(&app.Out, "out", "-", "Output file (\"-\" for stdout)")
   173  	flag.Parse()
   174  
   175  	cfg := &Config{
   176  		TenantID:      defaultTenantID,
   177  		ClientID:      defaultClientID,
   178  		ExtensionName: defaultExtensionName,
   179  	}
   180  	if config != "" {
   181  		b, err := ioutil.ReadFile(config)
   182  		if err != nil {
   183  			log.Fatal(err)
   184  		}
   185  		err = jsonx.Unmarshal(b, cfg)
   186  		if err != nil {
   187  			log.Fatal(err)
   188  		}
   189  	}
   190  	if app.TenantID == "" {
   191  		app.TenantID = cfg.TenantID
   192  	}
   193  	if app.ClientID == "" {
   194  		app.ClientID = cfg.ClientID
   195  	}
   196  	if app.ClientSecret == "" {
   197  		app.ClientSecret = cfg.ClientSecret
   198  	}
   199  	if app.TokenCache == "" {
   200  		app.TokenCache = cfg.TokenCache
   201  	}
   202  	if app.ExtensionName == "" {
   203  		app.ExtensionName = cfg.ExtensionName
   204  	}
   205  	app.LoginMap = cfg.LoginMap
   206  
   207  	var err error
   208  	ctx := context.Background()
   209  	switch strings.ToLower(app.Op) {
   210  	case "set":
   211  		err = app.Set(ctx)
   212  	case "get":
   213  		err = app.Get(ctx)
   214  	case "delete":
   215  		err = app.Delete(ctx)
   216  	default:
   217  		flag.CommandLine.SetOutput(os.Stderr)
   218  		flag.Usage()
   219  		os.Exit(1)
   220  	}
   221  	if err != nil {
   222  		log.Fatal(err)
   223  		os.Exit(1)
   224  	}
   225  }