Skip to content

Commit 61c6c04

Browse files
Merge dev into master
2 parents 1d24577 + d6f889a commit 61c6c04

17 files changed

+1522
-117
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,3 @@ jobs:
4040
4141
- name: Run Static Analyzer
4242
run: go vet -v ./...
43-
44-
gopath:
45-
name: Gopath build
46-
runs-on: ubuntu-latest
47-
env:
48-
GOPATH: ${{ github.workspace }}/go
49-
GO111MODULE: off
50-
51-
steps:
52-
- name: Set up Go 1.17
53-
uses: actions/setup-go@v3
54-
with:
55-
go-version: 1.17
56-
57-
- name: Check out code into GOPATH
58-
uses: actions/checkout@v2
59-
with:
60-
path: go/src/firebase.google.com/go
61-
62-
- name: Get dependencies
63-
run: go get -t -v $(go list ./... | grep -v integration)
64-
65-
- name: Run Unit Tests
66-
run: go test -v -race -test.short firebase.google.com/go/...

auth/auth.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,13 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
148148
userManagementEndpoint := idToolkitV1Endpoint
149149
providerConfigEndpoint := idToolkitV2Endpoint
150150
tenantMgtEndpoint := idToolkitV2Endpoint
151+
projectMgtEndpoint := idToolkitV2Endpoint
151152

152153
base := &baseClient{
153154
userManagementEndpoint: userManagementEndpoint,
154155
providerConfigEndpoint: providerConfigEndpoint,
155156
tenantMgtEndpoint: tenantMgtEndpoint,
157+
projectMgtEndpoint: projectMgtEndpoint,
156158
projectID: conf.ProjectID,
157159
httpClient: hc,
158160
idTokenVerifier: idTokenVerifier,
@@ -274,6 +276,7 @@ type baseClient struct {
274276
userManagementEndpoint string
275277
providerConfigEndpoint string
276278
tenantMgtEndpoint string
279+
projectMgtEndpoint string
277280
projectID string
278281
tenantID string
279282
httpClient *internal.HTTPClient

auth/multi_factor_config_mgt.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2023 Google Inc. All Rights Reserved.
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 auth
16+
17+
import (
18+
"fmt"
19+
)
20+
21+
// ProviderConfig represents a multi-factor auth provider configuration.
22+
// Currently, only TOTP is supported.
23+
type ProviderConfig struct {
24+
// The state of multi-factor configuration, whether it's enabled or disabled.
25+
State MultiFactorConfigState `json:"state"`
26+
// TOTPProviderConfig holds the TOTP (time-based one-time password) configuration that is used in second factor authentication.
27+
TOTPProviderConfig *TOTPProviderConfig `json:"totpProviderConfig,omitempty"`
28+
}
29+
30+
// TOTPProviderConfig represents configuration settings for TOTP second factor auth.
31+
type TOTPProviderConfig struct {
32+
// The number of adjacent intervals used by TOTP.
33+
AdjacentIntervals int `json:"adjacentIntervals,omitempty"`
34+
}
35+
36+
// MultiFactorConfigState represents whether the multi-factor configuration is enabled or disabled.
37+
type MultiFactorConfigState string
38+
39+
// These constants represent the possible values for the MultiFactorConfigState type.
40+
const (
41+
Enabled MultiFactorConfigState = "ENABLED"
42+
Disabled MultiFactorConfigState = "DISABLED"
43+
)
44+
45+
// MultiFactorConfig represents a multi-factor configuration for a tenant or project.
46+
// This can be used to define whether multi-factor authentication is enabled or disabled and the list of second factor challenges that are supported.
47+
type MultiFactorConfig struct {
48+
// A slice of pointers to ProviderConfig structs, each outlining the specific second factor authorization method.
49+
ProviderConfigs []*ProviderConfig `json:"providerConfigs,omitempty"`
50+
}
51+
52+
func (mfa *MultiFactorConfig) validate() error {
53+
if mfa == nil {
54+
return nil
55+
}
56+
if len(mfa.ProviderConfigs) == 0 {
57+
return fmt.Errorf("\"ProviderConfigs\" must be a non-empty array of type \"ProviderConfig\"s")
58+
}
59+
for _, providerConfig := range mfa.ProviderConfigs {
60+
if providerConfig == nil {
61+
return fmt.Errorf("\"ProviderConfigs\" must be a non-empty array of type \"ProviderConfig\"s")
62+
}
63+
if err := providerConfig.validate(); err != nil {
64+
return err
65+
}
66+
}
67+
return nil
68+
}
69+
70+
func (pvc *ProviderConfig) validate() error {
71+
if pvc.State == "" && pvc.TOTPProviderConfig == nil {
72+
return fmt.Errorf("\"ProviderConfig\" must be defined")
73+
}
74+
state := string(pvc.State)
75+
if state != string(Enabled) && state != string(Disabled) {
76+
return fmt.Errorf("\"ProviderConfig.State\" must be 'Enabled' or 'Disabled'")
77+
}
78+
return pvc.TOTPProviderConfig.validate()
79+
}
80+
81+
func (tpvc *TOTPProviderConfig) validate() error {
82+
if tpvc == nil {
83+
return fmt.Errorf("\"TOTPProviderConfig\" must be defined")
84+
}
85+
if !(tpvc.AdjacentIntervals >= 1 && tpvc.AdjacentIntervals <= 10) {
86+
return fmt.Errorf("\"AdjacentIntervals\" must be an integer between 1 and 10 (inclusive)")
87+
}
88+
return nil
89+
}

auth/multi_factor_config_mgt_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright 2023 Google Inc. All Rights Reserved.
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 auth
16+
17+
import (
18+
"testing"
19+
)
20+
21+
func TestMultiFactorConfig(t *testing.T) {
22+
mfa := MultiFactorConfig{
23+
ProviderConfigs: []*ProviderConfig{{
24+
State: Disabled,
25+
TOTPProviderConfig: &TOTPProviderConfig{
26+
AdjacentIntervals: 5,
27+
},
28+
}},
29+
}
30+
if err := mfa.validate(); err != nil {
31+
t.Errorf("MultiFactorConfig not valid")
32+
}
33+
}
34+
func TestMultiFactorConfigNoProviderConfigs(t *testing.T) {
35+
mfa := MultiFactorConfig{}
36+
want := "\"ProviderConfigs\" must be a non-empty array of type \"ProviderConfig\"s"
37+
if err := mfa.validate(); err.Error() != want {
38+
t.Errorf("MultiFactorConfig.validate(nil) = %v, want = %q", err, want)
39+
}
40+
}
41+
42+
func TestMultiFactorConfigNilProviderConfigs(t *testing.T) {
43+
mfa := MultiFactorConfig{
44+
ProviderConfigs: nil,
45+
}
46+
want := "\"ProviderConfigs\" must be a non-empty array of type \"ProviderConfig\"s"
47+
if err := mfa.validate(); err.Error() != want {
48+
t.Errorf("MultiFactorConfig.validate(nil) = %v, want = %q", err, want)
49+
}
50+
}
51+
52+
func TestMultiFactorConfigNilProviderConfig(t *testing.T) {
53+
mfa := MultiFactorConfig{
54+
ProviderConfigs: []*ProviderConfig{nil},
55+
}
56+
want := "\"ProviderConfigs\" must be a non-empty array of type \"ProviderConfig\"s"
57+
if err := mfa.validate(); err.Error() != want {
58+
t.Errorf("MultiFactorConfig.validate(nil) = %v, want = %q", err, want)
59+
}
60+
}
61+
62+
func TestMultiFactorConfigUndefinedProviderConfig(t *testing.T) {
63+
mfa := MultiFactorConfig{
64+
ProviderConfigs: []*ProviderConfig{{}},
65+
}
66+
want := "\"ProviderConfig\" must be defined"
67+
if err := mfa.validate(); err.Error() != want {
68+
t.Errorf("MultiFactorConfig.validate(nil) = %v, want = %q", err, want)
69+
}
70+
}
71+
72+
func TestMultiFactorConfigInvalidProviderConfigState(t *testing.T) {
73+
mfa := MultiFactorConfig{
74+
ProviderConfigs: []*ProviderConfig{{
75+
State: "invalid",
76+
}},
77+
}
78+
want := "\"ProviderConfig.State\" must be 'Enabled' or 'Disabled'"
79+
if err := mfa.validate(); err.Error() != want {
80+
t.Errorf("MultiFactorConfig.validate(nil) = %v, want = %q", err, want)
81+
}
82+
}
83+
84+
func TestMultiFactorConfigNilTOTPProviderConfig(t *testing.T) {
85+
mfa := MultiFactorConfig{
86+
ProviderConfigs: []*ProviderConfig{{
87+
State: Disabled,
88+
TOTPProviderConfig: nil,
89+
}},
90+
}
91+
want := "\"TOTPProviderConfig\" must be defined"
92+
if err := mfa.validate(); err.Error() != want {
93+
t.Errorf("MultiFactorConfig.validate(nil) = %v, want = %q", err, want)
94+
}
95+
}
96+
97+
func TestMultiFactorConfigInvalidAdjacentIntervals(t *testing.T) {
98+
mfa := MultiFactorConfig{
99+
ProviderConfigs: []*ProviderConfig{{
100+
State: Disabled,
101+
TOTPProviderConfig: &TOTPProviderConfig{
102+
AdjacentIntervals: 11,
103+
},
104+
}},
105+
}
106+
want := "\"AdjacentIntervals\" must be an integer between 1 and 10 (inclusive)"
107+
if err := mfa.validate(); err.Error() != want {
108+
t.Errorf("MultiFactorConfig.validate(nil) = %v, want = %q", err, want)
109+
}
110+
}

auth/project_config_mgt.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2023 Google Inc. All Rights Reserved.
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 auth
16+
17+
import (
18+
"context"
19+
"errors"
20+
"fmt"
21+
"net/http"
22+
"strings"
23+
24+
"firebase.google.com/go/v4/internal"
25+
)
26+
27+
// ProjectConfig represents the properties to update on the provided project config.
28+
type ProjectConfig struct {
29+
MultiFactorConfig *MultiFactorConfig `json:"mfa,omitEmpty"`
30+
}
31+
32+
func (base *baseClient) GetProjectConfig(ctx context.Context) (*ProjectConfig, error) {
33+
req := &internal.Request{
34+
Method: http.MethodGet,
35+
URL: "/config",
36+
}
37+
var result ProjectConfig
38+
if _, err := base.makeRequest(ctx, req, &result); err != nil {
39+
return nil, err
40+
}
41+
return &result, nil
42+
}
43+
44+
func (base *baseClient) UpdateProjectConfig(ctx context.Context, projectConfig *ProjectConfigToUpdate) (*ProjectConfig, error) {
45+
if projectConfig == nil {
46+
return nil, errors.New("project config must not be nil")
47+
}
48+
if err := projectConfig.validate(); err != nil {
49+
return nil, err
50+
}
51+
mask := projectConfig.params.UpdateMask()
52+
if len(mask) == 0 {
53+
return nil, errors.New("no parameters specified in the update request")
54+
}
55+
req := &internal.Request{
56+
Method: http.MethodPatch,
57+
URL: "/config",
58+
Body: internal.NewJSONEntity(projectConfig.params),
59+
Opts: []internal.HTTPOption{
60+
internal.WithQueryParam("updateMask", strings.Join(mask, ",")),
61+
},
62+
}
63+
var result ProjectConfig
64+
if _, err := base.makeRequest(ctx, req, &result); err != nil {
65+
return nil, err
66+
}
67+
return &result, nil
68+
}
69+
70+
// ProjectConfigToUpdate represents the options used to update the current project.
71+
type ProjectConfigToUpdate struct {
72+
params nestedMap
73+
}
74+
75+
const (
76+
multiFactorConfigProjectKey = "mfa"
77+
)
78+
79+
// MultiFactorConfig configures the project's multi-factor settings
80+
func (pc *ProjectConfigToUpdate) MultiFactorConfig(multiFactorConfig MultiFactorConfig) *ProjectConfigToUpdate {
81+
return pc.set(multiFactorConfigProjectKey, multiFactorConfig)
82+
}
83+
84+
func (pc *ProjectConfigToUpdate) set(key string, value interface{}) *ProjectConfigToUpdate {
85+
pc.ensureParams().Set(key, value)
86+
return pc
87+
}
88+
89+
func (pc *ProjectConfigToUpdate) ensureParams() nestedMap {
90+
if pc.params == nil {
91+
pc.params = make(nestedMap)
92+
}
93+
return pc.params
94+
}
95+
96+
func (pc *ProjectConfigToUpdate) validate() error {
97+
req := make(map[string]interface{})
98+
for k, v := range pc.params {
99+
req[k] = v
100+
}
101+
val, ok := req[multiFactorConfigProjectKey]
102+
if ok {
103+
multiFactorConfig, ok := val.(MultiFactorConfig)
104+
if !ok {
105+
return fmt.Errorf("invalid type for MultiFactorConfig: %s", req[multiFactorConfigProjectKey])
106+
}
107+
if err := multiFactorConfig.validate(); err != nil {
108+
return err
109+
}
110+
}
111+
return nil
112+
}

0 commit comments

Comments
 (0)