func maybeRemapCloudSQL(host string) (out string, err error) { if !strings.HasSuffix(host, cloudSQLSuffix) { return host, nil } inst := strings.TrimSuffix(host, cloudSQLSuffix) if !metadata.OnGCE() { return "", errors.New("CloudSQL support only available when running on Google Compute Engine.") } proj, err := metadata.ProjectID() if err != nil { return "", fmt.Errorf("Failed to lookup GCE project ID: %v", err) } admin, _ := sqladmin.New(oauth2.NewClient(context.Background(), google.ComputeTokenSource(""))) listRes, err := admin.Instances.List(proj).Do() if err != nil { return "", fmt.Errorf("error enumerating Cloud SQL instances: %v", err) } for _, it := range listRes.Items { if !strings.EqualFold(it.Instance, inst) { continue } js, _ := json.Marshal(it) log.Printf("Found Cloud SQL instance %s: %s", inst, js) for _, ipm := range it.IpAddresses { return ipm.IpAddress, nil } return "", fmt.Errorf("No external IP address for Cloud SQL instances %s", inst) } var found []string for _, it := range listRes.Items { found = append(found, it.Instance) } return "", fmt.Errorf("Cloud SQL instance %q not found. Found: %q", inst, found) }
// DefaultClient returns an HTTP Client that uses the // DefaultTokenSource to obtain authentication credentials. // // This client should be used when developing services // that run on Google App Engine or Google Compute Engine // and use "Application Default Credentials." // // For more details, see: // https://developers.google.com/accounts/application-default-credentials // func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) { ts, err := DefaultTokenSource(ctx, scope...) if err != nil { return nil, err } return oauth2.NewClient(ctx, ts), nil }
func (c *gceCmd) RunCommand(args []string) error { if c.verbose { gce.Verbose = true } if c.project == "" { return cmdmain.UsageError("Missing --project flag.") } if (c.certFile == "") != (c.keyFile == "") { return cmdmain.UsageError("--cert and --key must both be given together.") } if c.certFile == "" && c.hostname == "" { return cmdmain.UsageError("Either --hostname, or --cert & --key must provided.") } config := gce.NewOAuthConfig(readFile(clientIdDat), readFile(clientSecretDat)) config.RedirectURL = "urn:ietf:wg:oauth:2.0:oob" instConf := &gce.InstanceConf{ Name: c.instName, Project: c.project, Machine: c.machine, Zone: c.zone, CertFile: c.certFile, KeyFile: c.keyFile, Hostname: c.hostname, } if c.sshPub != "" { instConf.SSHPub = strings.TrimSpace(readFile(c.sshPub)) } depl := &gce.Deployer{ Client: oauth2.NewClient(oauth2.NoContext, oauth2.ReuseTokenSource(nil, &oauthutil.TokenSource{ Config: config, CacheFile: c.project + "-token.json", AuthCode: func() string { fmt.Println("Get auth code from:") fmt.Printf("%v\n", config.AuthCodeURL("my-state", oauth2.AccessTypeOffline, oauth2.ApprovalForce)) fmt.Println("Enter auth code:") sc := bufio.NewScanner(os.Stdin) sc.Scan() return strings.TrimSpace(sc.Text()) }, })), Conf: instConf, } inst, err := depl.Create(context.TODO()) if err != nil { return err } log.Printf("Instance is up at %s", inst.NetworkInterfaces[0].AccessConfigs[0].NatIP) return nil }
// NewServiceClient returns a Client for use when running on Google // Compute Engine. This client can access buckets owned by the same // project ID as the VM. func NewServiceClient() (*Client, error) { if !metadata.OnGCE() { return nil, errors.New("not running on Google Compute Engine") } scopes, _ := metadata.Scopes("default") haveScope := func(scope string) bool { for _, x := range scopes { if x == scope { return true } } return false } if !haveScope("https://www.googleapis.com/auth/devstorage.full_control") && !haveScope("https://www.googleapis.com/auth/devstorage.read_write") { return nil, errors.New("when this Google Compute Engine VM instance was created, it wasn't granted access to Cloud Storage") } client := oauth2.NewClient(context.Background(), google.ComputeTokenSource("")) service, _ := api.New(client) return &Client{client: client, service: service}, nil }
func newFromConfig(_ blobserver.Loader, config jsonconfig.Obj) (blobserver.Storage, error) { var ( auth = config.RequiredObject("auth") bucket = config.RequiredString("bucket") cacheSize = config.OptionalInt64("cacheSize", 32<<20) clientID = auth.RequiredString("client_id") // or "auto" for service accounts clientSecret = auth.OptionalString("client_secret", "") refreshToken = auth.OptionalString("refresh_token", "") ) if err := config.Validate(); err != nil { return nil, err } if err := auth.Validate(); err != nil { return nil, err } var dirPrefix string if parts := strings.SplitN(bucket, "/", 2); len(parts) > 1 { dirPrefix = parts[1] bucket = parts[0] } if dirPrefix != "" && !strings.HasSuffix(dirPrefix, "/") { dirPrefix += "/" } gs := &Storage{ bucket: bucket, dirPrefix: dirPrefix, } if clientID == "auto" { var err error gs.client, err = googlestorage.NewServiceClient() if err != nil { return nil, err } } else { if clientSecret == "" { return nil, errors.New("missing required parameter 'client_secret'") } if refreshToken == "" { return nil, errors.New("missing required parameter 'refresh_token'") } oAuthClient := oauth2.NewClient(oauth2.NoContext, oauthutil.NewRefreshTokenSource(&oauth2.Config{ Scopes: []string{googlestorage.Scope}, Endpoint: google.Endpoint, ClientID: clientID, ClientSecret: clientSecret, RedirectURL: oauthutil.TitleBarRedirectURL, }, refreshToken)) gs.client = googlestorage.NewClient(oAuthClient) } if cacheSize != 0 { gs.cache = memory.NewCache(cacheSize) } bi, err := gs.client.BucketInfo(bucket) if err != nil { return nil, fmt.Errorf("error statting bucket %q: %v", bucket, err) } hash := sha1.New() fmt.Fprintf(hash, "%v%v", bi.TimeCreated, bi.Metageneration) gs.genRandom = fmt.Sprintf("%x", hash.Sum(nil)) gs.genTime, _ = time.Parse(time.RFC3339, bi.TimeCreated) return gs, nil }
func uploadDockerImage() { proj := "camlistore-website" bucket := "camlistore-release" versionedTarball := "docker/camlistored-" + *rev + ".tar.gz" tarball := "docker/camlistored.tar.gz" log.Printf("Uploading %s/%s ...", bucket, versionedTarball) ts, err := tokenSource(bucket) if err != nil { log.Fatal(err) } httpClient := oauth2.NewClient(oauth2.NoContext, ts) ctx := cloud.NewContext(proj, httpClient) w := storage.NewWriter(ctx, bucket, versionedTarball) // If you don't give the owners access, the web UI seems to // have a bug and doesn't have access to see that it's public, so // won't render the "Shared Publicly" link. So we do that, even // though it's dumb and unnecessary otherwise: acl := append(w.ACL, storage.ACLRule{Entity: storage.ACLEntity("project-owners-" + proj), Role: storage.RoleOwner}) acl = append(acl, storage.ACLRule{Entity: storage.AllUsers, Role: storage.RoleReader}) w.ACL = acl w.CacheControl = "no-cache" // TODO: remove for non-tip releases? set expirations? w.ContentType = "application/x-gtar" dockerSave := exec.Command("docker", "save", "camlistore/server") dockerSave.Stderr = os.Stderr tar, err := dockerSave.StdoutPipe() if err != nil { log.Fatal(err) } targz, pw := io.Pipe() go func() { zw := gzip.NewWriter(pw) n, err := io.Copy(zw, tar) if err != nil { log.Fatalf("Error copying to gzip writer: after %d bytes, %v", n, err) } if err := zw.Close(); err != nil { log.Fatalf("gzip.Close: %v", err) } pw.CloseWithError(err) }() if err := dockerSave.Start(); err != nil { log.Fatalf("Error starting docker save camlistore/server: %v", err) } if _, err := io.Copy(w, targz); err != nil { log.Fatalf("io.Copy: %v", err) } if err := w.Close(); err != nil { log.Fatalf("closing GCS storage writer: %v", err) } if err := dockerSave.Wait(); err != nil { log.Fatalf("Error waiting for docker save camlistore/server: %v", err) } log.Printf("Uploaded tarball to %s", versionedTarball) log.Printf("Copying tarball to %s/%s ...", bucket, tarball) // TODO(mpl): 2015-05-12: update google.golang.org/cloud/storage so we // can specify the dest name in CopyObject, and we get the ACLs from the // src for free too I think. if _, err := storage.CopyObject(ctx, bucket, versionedTarball, bucket, storage.ObjectAttrs{ Name: tarball, ACL: acl, ContentType: "application/x-gtar", }); err != nil { log.Fatalf("Error uploading %v: %v", tarball, err) } log.Printf("Uploaded tarball to %s", tarball) }
func (js jwtSource) Token() (*oauth2.Token, error) { pk, err := internal.ParseKey(js.conf.PrivateKey) if err != nil { return nil, err } hc := oauth2.NewClient(js.ctx, nil) claimSet := &jws.ClaimSet{ Iss: js.conf.Email, Scope: strings.Join(js.conf.Scopes, " "), Aud: js.conf.TokenURL, } if subject := js.conf.Subject; subject != "" { claimSet.Sub = subject // prn is the old name of sub. Keep setting it // to be compatible with legacy OAuth 2.0 providers. claimSet.Prn = subject } payload, err := jws.Encode(defaultHeader, claimSet, pk) if err != nil { return nil, err } v := url.Values{} v.Set("grant_type", defaultGrantType) v.Set("assertion", payload) resp, err := hc.PostForm(js.conf.TokenURL, v) if err != nil { return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) if err != nil { return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) } if c := resp.StatusCode; c < 200 || c > 299 { return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body) } // tokenRes is the JSON response body. var tokenRes struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` IDToken string `json:"id_token"` ExpiresIn int64 `json:"expires_in"` // relative seconds from now } if err := json.Unmarshal(body, &tokenRes); err != nil { return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) } token := &oauth2.Token{ AccessToken: tokenRes.AccessToken, TokenType: tokenRes.TokenType, } raw := make(map[string]interface{}) json.Unmarshal(body, &raw) // no error checks for optional fields token = token.WithExtra(raw) if secs := tokenRes.ExpiresIn; secs > 0 { token.Expiry = time.Now().Add(time.Duration(secs) * time.Second) } if v := tokenRes.IDToken; v != "" { // decode returned id token to get expiry claimSet, err := jws.Decode(v) if err != nil { return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err) } token.Expiry = time.Unix(claimSet.Exp, 0) } return token, nil }
// Client returns an HTTP client wrapping the context's // HTTP transport and adding Authorization headers with tokens // obtained from c. // // The returned client and its Transport should not be modified. func (c *Config) Client(ctx context.Context) *http.Client { return oauth2.NewClient(ctx, c.TokenSource(ctx)) }