go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/gerritauth/module.go (about)

     1  // Copyright 2021 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package gerritauth
    16  
    17  import (
    18  	"context"
    19  	"flag"
    20  
    21  	"go.chromium.org/luci/common/errors"
    22  	luciflag "go.chromium.org/luci/common/flag"
    23  	"go.chromium.org/luci/common/logging"
    24  
    25  	"go.chromium.org/luci/server/module"
    26  	"go.chromium.org/luci/server/warmup"
    27  )
    28  
    29  // ModuleName can be used to refer to this module when declaring dependencies.
    30  var ModuleName = module.RegisterName("go.chromium.org/luci/server/gerritauth")
    31  
    32  // ModuleOptions contain configuration of the gerritauth server module.
    33  type ModuleOptions struct {
    34  	// Method is an instance of AuthMethod to configure.
    35  	//
    36  	// If nil, will configure the global Method instance.
    37  	Method *AuthMethod
    38  
    39  	// Header is a name of the request header to check for JWTs.
    40  	//
    41  	// Default is "X-Gerrit-Auth".
    42  	Header string
    43  
    44  	// SignerAccounts are emails of services account that sign Gerrit JWTs.
    45  	//
    46  	// If empty, authentication based on Gerrit JWTs will be disabled.
    47  	SignerAccounts []string
    48  
    49  	// Audience is an expected "aud" field of JWTs.
    50  	//
    51  	// Required if SignerAccount is not empty.
    52  	Audience string
    53  }
    54  
    55  // Register registers the command line flags.
    56  func (o *ModuleOptions) Register(f *flag.FlagSet) {
    57  	if o.Header == "" {
    58  		o.Header = "X-Gerrit-Auth"
    59  	}
    60  	f.StringVar(
    61  		&o.Header,
    62  		"gerrit-auth-header",
    63  		o.Header,
    64  		`Name of the request header to check for JWTs.`,
    65  	)
    66  	f.Var(luciflag.StringSlice(&o.SignerAccounts),
    67  		"gerrit-auth-signer-account",
    68  		"Email of a Gerrit service account trusted to sign Gerrit JWT tokens. "+
    69  			"May be specified multiple times. If empty, authentication based on Gerrit JWTs will be disabled.",
    70  	)
    71  	f.StringVar(
    72  		&o.Audience,
    73  		"gerrit-auth-audience",
    74  		o.Audience,
    75  		`Expected "aud" field of the JWTs. Required if -gerrit-auth-signer-account is used.`,
    76  	)
    77  }
    78  
    79  // NewModule returns a server module that configures Gerrit auth method.
    80  func NewModule(opts *ModuleOptions) module.Module {
    81  	if opts == nil {
    82  		opts = &ModuleOptions{}
    83  	}
    84  	return &serverModule{opts: opts}
    85  }
    86  
    87  // NewModuleFromFlags is a variant of NewModule that initializes options through
    88  // command line flags.
    89  //
    90  // Calling this function registers flags in flag.CommandLine. They are usually
    91  // parsed in server.Main(...).
    92  func NewModuleFromFlags() module.Module {
    93  	opts := &ModuleOptions{}
    94  	opts.Register(flag.CommandLine)
    95  	return NewModule(opts)
    96  }
    97  
    98  // serverModule implements module.Module.
    99  type serverModule struct {
   100  	opts *ModuleOptions
   101  }
   102  
   103  // Name is part of module.Module interface.
   104  func (*serverModule) Name() module.Name {
   105  	return ModuleName
   106  }
   107  
   108  // Dependencies is part of module.Module interface.
   109  func (*serverModule) Dependencies() []module.Dependency {
   110  	return nil
   111  }
   112  
   113  // Initialize is part of module.Module interface.
   114  func (m *serverModule) Initialize(ctx context.Context, host module.Host, opts module.HostOptions) (context.Context, error) {
   115  	if len(m.opts.SignerAccounts) != 0 {
   116  		if m.opts.Audience == "" {
   117  			return nil, errors.Reason("-gerrit-auth-audience is required when -gerrit-auth-signer-account is used").Err()
   118  		}
   119  	} else if opts.Prod {
   120  		logging.Warningf(ctx, "Disabling Gerrit JWT auth: -gerrit-auth-signer-account is unset")
   121  	}
   122  
   123  	method := m.opts.Method
   124  	if method == nil {
   125  		method = &Method
   126  	}
   127  	method.Header = m.opts.Header
   128  	method.SignerAccounts = m.opts.SignerAccounts
   129  	method.Audience = m.opts.Audience
   130  
   131  	if method.isConfigured() {
   132  		warmup.Register("server/gerritauth", method.Warmup)
   133  	}
   134  	return ctx, nil
   135  }