@@ 145,6 145,65 @@ func authForUsername(ctx context.Context, username string) (*AuthContext, error)
return &auth, nil
}
+func authForOAuthClient(ctx context.Context, clientUUID string) (*AuthContext, error) {
+ var auth AuthContext
+ if err := database.WithTx(ctx, &sql.TxOptions{
+ Isolation: 0,
+ ReadOnly: true,
+ }, func(tx *sql.Tx) error {
+ var (
+ err error
+ rows *sql.Rows
+ )
+ query := database.
+ Select(ctx, []string{
+ `u.id`, `u.username`,
+ `u.created`, `u.updated`,
+ `u.email`,
+ `u.user_type`,
+ `u.url`, `u.location`, `u.bio`,
+ `u.suspension_notice`,
+ }).
+ From(`"oauth2_client" client`).
+ Join(`"user" u ON u.id = client.owner_id`).
+ Where(`client.client_uuid = ?`, clientUUID)
+ if rows, err = query.RunWith(tx).Query(); err != nil {
+ panic(err)
+ }
+ defer rows.Close()
+
+ if !rows.Next() {
+ if err := rows.Err(); err != nil {
+ panic(err)
+ }
+ return fmt.Errorf("Authenticating for unknown client ID %s", clientUUID)
+ }
+ if err := rows.Scan(&auth.UserID, &auth.Username, &auth.Created,
+ &auth.Updated, &auth.Email, &auth.UserType, &auth.URL, &auth.Location,
+ &auth.Bio, &auth.SuspensionNotice); err != nil {
+ panic(err)
+ }
+ if rows.Next() {
+ // TODO: Fetch user info from meta if necessary
+ if err := rows.Err(); err != nil {
+ panic(err)
+ }
+ panic(errors.New("Multiple matching user accounts; invariant broken"))
+ }
+ return nil
+ }); err != nil {
+ return nil, err
+ }
+
+ if auth.UserType == USER_SUSPENDED {
+ return nil, fmt.Errorf(
+ "Account suspended with the following notice: %s\nContact support",
+ auth.SuspensionNotice)
+ }
+
+ return &auth, nil
+}
+
type AuthCookie struct {
// The username of the authenticated user
Name string `json:"name"`
@@ 185,6 244,9 @@ type InternalAuth struct {
// An arbitrary identifier for this internal node, e.g. "us-east-3.git.sr.ht"
NodeID string `json:"node_id"`
+
+ // Only used by specific meta.sr.ht routes
+ OAuthClientUUID string `json:"oauth_client_id",omit-empty`
}
func internalAuth(internalNet []*net.IPNet, payload []byte,
@@ 224,7 286,12 @@ func internalAuth(internalNet []*net.IPNet, payload []byte,
authError(w, "Invalid Authorization header", http.StatusForbidden)
}
- auth, err := authForUsername(r.Context(), internalAuth.Name)
+ var auth *AuthContext
+ if internalAuth.OAuthClientUUID != "" {
+ auth, err = authForOAuthClient(r.Context(), internalAuth.OAuthClientUUID)
+ } else {
+ auth, err = authForUsername(r.Context(), internalAuth.Name)
+ }
if err != nil {
authError(w, err.Error(), http.StatusForbidden)
return
@@ 472,7 539,7 @@ func OAuth2(token string, hash [64]byte, w http.ResponseWriter,
if ot.Grants != "" {
auth.Access = make(map[string]string)
- for _, grant := range strings.Split(ot.Grants, ",") {
+ for _, grant := range strings.Split(ot.Grants, " ") {
var (
service string
scope string
@@ 480,7 547,7 @@ func OAuth2(token string, hash [64]byte, w http.ResponseWriter,
)
parts := strings.Split(grant, "/")
if len(parts) != 2 {
- panic(errors.New("OAuth grant without service/scope format"))
+ panic(fmt.Errorf("OAuth grant '%s' without service/scope format", grant))
}
service = parts[0]
parts = strings.Split(parts[1], ":")