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 }