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 }