go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/appengine/gaeauth/server/db.go (about)

     1  // Copyright 2015 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 server
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  
    21  	"go.chromium.org/luci/appengine/gaeauth/server/internal/authdbimpl"
    22  	"go.chromium.org/luci/common/clock"
    23  	"go.chromium.org/luci/common/logging"
    24  	"go.chromium.org/luci/gae/service/info"
    25  	"go.chromium.org/luci/server/auth/authdb"
    26  )
    27  
    28  // errNotConfigured is returned on real GAE if auth service URL is not set.
    29  var errNotConfigured = errors.New(
    30  	"Auth Service URL is not configured, you MUST configure it for apps used " +
    31  		"in production, visit /admin/portal/auth_service to do so.")
    32  
    33  // GetAuthDB fetches AuthDB snapshot from the datastore and returns authdb.DB
    34  // interface wrapping it.
    35  //
    36  // It may reuse existing one (`prev`), if no changes were made. If `prev` is
    37  // nil, always fetches a new copy from the datastore.
    38  //
    39  // If auth_service URL is not configured, returns special kind of authdb.DB that
    40  // implements some default authorization rules (allow everything on dev server,
    41  // forbid everything and emit errors on real GAE).
    42  func GetAuthDB(ctx context.Context, prev authdb.DB) (authdb.DB, error) {
    43  	// Grab revision number of most recent snapshot.
    44  	latest, err := authdbimpl.GetLatestSnapshotInfo(ctx)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	// If auth_service URL is not configured, use default db implementation.
    50  	if latest == nil {
    51  		if info.IsDevAppServer(ctx) {
    52  			return authdb.DevServerDB{}, nil
    53  		}
    54  		return authdb.ErroringDB{Error: errNotConfigured}, nil
    55  	}
    56  
    57  	// No newer version in the datastore? Reuse what we have in memory. `prev` may
    58  	// be an instance of ErroringDB or DevServerDB, so use non-panicking type
    59  	// assertion.
    60  	if prevDB, _ := prev.(*authdb.SnapshotDB); prevDB != nil {
    61  		if prevDB.AuthServiceURL == latest.AuthServiceURL && prevDB.Rev == latest.Rev {
    62  			return prevDB, nil
    63  		}
    64  	}
    65  
    66  	// Fetch new snapshot from the datastore. It was validated already when it was
    67  	// stored, so skip expensive validation step. Log how long it takes to keep an
    68  	// eye on performance here, since it has potential to become slow.
    69  	start := clock.Now(ctx)
    70  	proto, err := authdbimpl.GetAuthDBSnapshot(ctx, latest.GetSnapshotID())
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	db, err := authdb.NewSnapshotDB(proto, latest.AuthServiceURL, latest.Rev, false)
    75  	logging.Infof(ctx, "auth: AuthDB at rev %d fetched in %s", latest.Rev, clock.Now(ctx).Sub(start))
    76  	if err != nil {
    77  		logging.Errorf(ctx, "auth: AuthDB is invalid - %s", err)
    78  		return nil, err
    79  	}
    80  
    81  	return db, nil
    82  }