This commit is contained in:
@@ -47,6 +47,7 @@ const (
|
||||
epistemicsDBPasswordPath = "/run/secrets/epistemics_db_password"
|
||||
interactionDBPasswordPath = "/run/secrets/interaction_db_password"
|
||||
selfDBPasswordPath = "/run/secrets/self_db_password"
|
||||
acceptanceHarnessTokenPath = "/run/secrets/acceptance_harness_service_token"
|
||||
controlPlaneServiceTokenFile = "control_plane_service_token"
|
||||
)
|
||||
|
||||
@@ -162,6 +163,18 @@ func main() {
|
||||
if err := bootstrapCreatorAccount(context.Background(), identityRepository, creatorUsername, creatorPasswordPath); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
acceptanceServiceToken := ""
|
||||
var acceptancePrincipal identity.ServicePrincipal
|
||||
if os.Getenv("ACCEPTANCE_HARNESS_SERVICE_CREDENTIAL_REF") != "" {
|
||||
acceptanceServiceToken, err = readCredentialRefSecretPurpose("ACCEPTANCE_HARNESS_SERVICE_TOKEN", "ACCEPTANCE_HARNESS_SERVICE_CREDENTIAL_REF", acceptanceHarnessTokenPath, secretcontracts.SecretPurposeServiceAuthentication)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
acceptancePrincipal, err = identityRepository.LoadServicePrincipal(context.Background(), identity.PrincipalID("domovoy-acceptance-harness-principal"))
|
||||
if err != nil || !acceptancePrincipal.Active {
|
||||
log.Fatal("acceptance service principal is unavailable")
|
||||
}
|
||||
}
|
||||
interactionPassword, err := readCredentialRefSecret("INTERACTION_POSTGRES_PASSWORD", "INTERACTION_POSTGRES_CREDENTIAL_REF", interactionDBPasswordPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -230,12 +243,32 @@ func main() {
|
||||
mux := http.NewServeMux()
|
||||
authenticateCreator := func(w http.ResponseWriter, r *http.Request) (identity.CreatorSession, bool) {
|
||||
session, err := identitydomain.ValidateCreatorSession(sessionKey, bearerToken(r), time.Now())
|
||||
if err != nil {
|
||||
if err != nil || (session.PrincipalKind != "" && session.PrincipalKind != identity.PrincipalKindHuman) {
|
||||
http.Error(w, "creator session denied", http.StatusUnauthorized)
|
||||
return identity.CreatorSession{}, false
|
||||
}
|
||||
return session, true
|
||||
}
|
||||
authenticateAcceptance := func(w http.ResponseWriter, r *http.Request) (identity.CreatorSession, bool) {
|
||||
if acceptanceServiceToken == "" || acceptancePrincipal.Kind != identity.PrincipalKindService || !acceptancePrincipal.Active || !identitydomain.BootstrapMatches(acceptanceServiceToken, bearerToken(r)) {
|
||||
http.Error(w, "acceptance authentication denied", http.StatusUnauthorized)
|
||||
return identity.CreatorSession{}, false
|
||||
}
|
||||
_, session, err := identitydomain.IssueServiceSession(sessionKey, acceptancePrincipal, time.Now(), 30*time.Minute)
|
||||
if err != nil {
|
||||
http.Error(w, "acceptance session unavailable", http.StatusServiceUnavailable)
|
||||
return identity.CreatorSession{}, false
|
||||
}
|
||||
return session, true
|
||||
}
|
||||
authenticateAcceptanceSession := func(w http.ResponseWriter, r *http.Request) (identity.CreatorSession, bool) {
|
||||
session, err := identitydomain.ValidateCreatorSession(sessionKey, bearerToken(r), time.Now())
|
||||
if err != nil || session.PrincipalKind != identity.PrincipalKindService || session.PrincipalID != acceptancePrincipal.PrincipalID || !acceptancePrincipal.Active {
|
||||
http.Error(w, "acceptance session denied", http.StatusUnauthorized)
|
||||
return identity.CreatorSession{}, false
|
||||
}
|
||||
return session, true
|
||||
}
|
||||
resolveFocus := func(r *http.Request) (situation.FocusBinding, error) {
|
||||
runtime := situation.RuntimeBinding{SessionID: situation.SessionID(r.URL.Query().Get("session_id")), Workspace: situation.WorkspaceID(r.URL.Query().Get("workspace_id"))}
|
||||
bindings, err := repository.ByRuntime(r.Context(), runtime)
|
||||
@@ -286,6 +319,35 @@ func main() {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
mux.HandleFunc("POST /v1/creator/sessions", creatorDatabaseSessionHandler(bootstrapToken, creatorUsername, identityRepository, sessionKey, time.Now))
|
||||
mux.HandleFunc("POST /v1/acceptance/sessions", func(w http.ResponseWriter, r *http.Request) {
|
||||
session, ok := authenticateAcceptance(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
token, issued, err := identitydomain.IssueServiceSession(sessionKey, acceptancePrincipal, time.Now(), 30*time.Minute)
|
||||
if err != nil {
|
||||
http.Error(w, "acceptance session unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
_ = session
|
||||
writeCreatorSession(w, token, issued)
|
||||
})
|
||||
mux.HandleFunc("GET /v1/acceptance/self/summary", func(w http.ResponseWriter, r *http.Request) {
|
||||
if _, ok := authenticateAcceptanceSession(w, r); !ok {
|
||||
return
|
||||
}
|
||||
if selfRepository == nil {
|
||||
http.Error(w, "SELF unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
summary, err := selfRepository.AcceptanceSummary(r.Context(), string(acceptancePrincipal.PrincipalID))
|
||||
if err != nil {
|
||||
http.Error(w, "SELF summary unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(summary)
|
||||
})
|
||||
mux.HandleFunc("POST /v1/creator/account/password", func(w http.ResponseWriter, r *http.Request) {
|
||||
session, ok := authenticateCreator(w, r)
|
||||
if !ok {
|
||||
@@ -596,15 +658,21 @@ func main() {
|
||||
}{Experiences: experiences, Reflections: reflections, Skills: skills})
|
||||
})
|
||||
mux.HandleFunc("POST /v1/creator/chat/messages", func(w http.ResponseWriter, r *http.Request) {
|
||||
var request interaction.ChatMessageRequest
|
||||
body, readErr := io.ReadAll(http.MaxBytesReader(w, r.Body, interactiondomain.MaxHumanUtteranceBytes+1024))
|
||||
if readErr != nil || json.Unmarshal(body, &request) != nil {
|
||||
http.Error(w, "invalid chat message", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
session, ok := authenticateCreator(w, r)
|
||||
if !ok && request.SyntheticAcceptance {
|
||||
session, ok = authenticateAcceptanceSession(w, r)
|
||||
}
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var request interaction.ChatMessageRequest
|
||||
decoder := json.NewDecoder(io.LimitReader(r.Body, interactiondomain.MaxHumanUtteranceBytes+1024))
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&request); err != nil || decoder.Decode(&struct{}{}) != io.EOF {
|
||||
http.Error(w, "invalid chat message", http.StatusBadRequest)
|
||||
if session.PrincipalKind == identity.PrincipalKindService && (!request.SyntheticAcceptance || request.SyntheticScopeID != acceptancePrincipal.SyntheticScope) {
|
||||
http.Error(w, "synthetic acceptance scope denied", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
|
||||
Reference in New Issue
Block a user