Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

develop to iv #215

Draft
wants to merge 42 commits into
base: iv
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
319b8af
Add missing quotes
alexfmpe Jun 1, 2022
df75577
Update thunk github.json in README.md
Jan 26, 2023
ce3bb5c
Merge branch 'develop' into aa/ghc810
ali-abrar May 23, 2023
05f1c04
Bump obelisk
ali-abrar May 23, 2023
a99a09b
Merge remote-tracking branch 'origin/deprecate-dot-obelisk' into aa/g…
ali-abrar May 25, 2023
3ff0e9f
Update changelog
ali-abrar May 25, 2023
8e4b533
Merge branch 'develop' into aa/ghc810
ali-abrar May 25, 2023
8e2ea67
Merge pull request #208 from obsidiansystems/cg/purify-token-decryption
ali-abrar May 25, 2023
29c5855
dep: remove unnecessary overrides; bump others
ali-abrar May 25, 2023
f82fc70
Merge branch 'aa/ghc810' of github.com:obsidiansystems/rhyolite into …
ali-abrar May 25, 2023
8922046
Merge remote-tracking branch 'origin/develop' into aa/ghc810
ali-abrar May 25, 2023
9115c92
deps: update deps
ali-abrar May 25, 2023
81976ba
Cut rc-1.0; update changelog
ali-abrar May 25, 2023
032e6d4
deps: obelisk -> 1.1.1.0
ali-abrar Aug 2, 2023
bcf0c1b
deps: update bytestring-aeson-orphans and aeson-qq
ali-abrar Aug 2, 2023
a6a0d02
deps: update beam-automigrate and postgresql-lo-stream
ali-abrar Aug 3, 2023
cf0718b
Merge pull request #204 from obsidiansystems/aa/ghc810
ali-abrar Aug 3, 2023
ec1333c
v1
ali-abrar Aug 3, 2023
d356ccd
Merge pull request #194 from obsidiansystems/readme-typo
ali-abrar Aug 3, 2023
ad64881
widgets: remove Reflex.Dom.Modal.*
ali-abrar Aug 23, 2023
35cef32
account: make it possible to use accounts without notify
ali-abrar Sep 1, 2023
bd2155d
account: make it possible to use accounts without notify
ali-abrar Sep 1, 2023
6c01dc9
Merge remote-tracking branch 'origin/develop' into develop
ali-abrar Sep 1, 2023
35c1063
account: Re-add login to export list
ali-abrar Sep 4, 2023
7589b06
account: Add AccountId type synonym
ali-abrar Sep 5, 2023
ab7e62e
obelisk 1.1 -> 1.3
ali-abrar Dec 29, 2023
8e0f328
Merge pull request #211 from obsidiansystems/aa/obelisk-1.3
ali-abrar Dec 29, 2023
997d875
Merge branch 'develop' into aa/remove-modals
ali-abrar Dec 29, 2023
79f21b0
Make authentication easier to use and fix some things about ErrorV
cgibbard May 8, 2024
9d9f2d2
Merge branch 'develop' into cg/fix-errorv
ali-abrar May 8, 2024
81752cc
Add a Path to ErrorV to make it easier to use on the frontend.
cgibbard May 8, 2024
27873bb
Merge remote-tracking branch 'origin/cg/fix-errorv' into cg/fix-errorv
cgibbard May 8, 2024
f022309
Const SelectedCount is more convenient on the frontend than Proxy for…
cgibbard May 9, 2024
de67d6e
Make observeErrorV more general and correct.
cgibbard May 9, 2024
7c80f95
Fix the type of errorV Path.
cgibbard May 9, 2024
3d77e73
Merge pull request #213 from obsidiansystems/cg/fix-errorv
ali-abrar May 25, 2024
1f29ec5
Merge pull request #210 from obsidiansystems/aa/remove-modals
ali-abrar May 25, 2024
4adb328
Update ChangeLog.md
ali-abrar May 25, 2024
8a952fb
Bump version
ali-abrar May 25, 2024
8a10a67
Merge branch 'release/2024-05-24'
ali-abrar May 25, 2024
0b06e21
Merge branch 'develop' into develop-to-iv
Ericson2314 Oct 2, 2024
496a0e3
Merge branch 'iv' into develop-to-iv
Ericson2314 Oct 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@
This project's release branch is `master`. This log is written from the perspective of the release branch: when changes hit `master`, they are considered released, and the date should reflect that release.

## Unreleased
(Creating a separate list, because the previous changes probably deserve their own release)

* Open source some "incremental view" infra (name is provision).

## v1.1.0.0 2024-05-24

* Breaking: Remove Reflex.Dom.Modal.Base and Reflex.Dom.Modal.Class. The `<dialog>` element is now broadly supported by browsers and provides a simpler solution to the problem of opening modals that is also more accessible. See the [documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog), and, in particular, this [example](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement#opening_a_modal_dialog), which uses `showModal` and describes how to style the modal backdrop.
* Breaking: [Make authentication easier to use and fix some things about ErrorV #213](https://github.com/obsidiansystems/rhyolite/pull/213)
* Make it possible to use Rhyolite.Backend.Account without notifications. See Rhyolite.Backend.Account.Db for versions of createAccount and ensureAccountExists that don't send notifications.
* Update to obelisk v1.3.0.0

## v1.0.0.0 2023-08-03
* Breaking: Drop groundhog support
* Breaking: Use Commutative from commutative-semigroups instead of Additive from patch
* Update to vessel-0.3
* Support ghc-8.10
* Add Data.Vessel.Void
* Move .obelisk/impl to dep/obelisk
* Breaking: handleAuthMapQuery and handlePersonalAuthMapQuery now take pure functions for decrypting user
tokens. This is fine in practice because it should almost always be readSignedWithKey from signed-data,
partially applied to a CSK. We had a major performance issue when someone stuck a database query inside
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,28 @@ Rhyolite provides:
{ system ? builtins.currentSystem, obelisk ? import ./.obelisk/impl {
inherit system;
iosSdkVersion = "13.2";

# You must accept the Android Software Development Kit License Agreement at
# https://developer.android.com/studio/terms in order to build Android apps.
# Uncomment and set this to `true` to indicate your acceptance:
# config.android_sdk.accept_license = false;

# In order to use Let's Encrypt for HTTPS deployments you must accept
# their terms of service at https://letsencrypt.org/repository/.
# Uncomment and set this to `true` to indicate your acceptance:
# terms.security.acme.acceptTerms = false;
} }:
with obelisk;
project ./. ({ pkgs, hackGet, ... }@args: {

overrides = pkgs.lib.composeExtensions
(pkgs.callPackage (hackGet ./dep/rhyolite) args).haskellOverrides
(self: super:
with pkgs.haskell.lib;
{
# Your custom overrides go here.
});

android.applicationId = "systems.obsidian.obelisk.examples.minimal";
android.displayName = "Obelisk Minimal Example";
ios.bundleIdentifier = "systems.obsidian.obelisk.examples.minimal";
Expand All @@ -74,8 +74,9 @@ with this Rhyolite thunk:
"owner": "obsidiansystems",
"repo": "rhyolite",
"branch": "master",
"rev": "06b9851a101408a86a4ec0b7df5b2f71bc532ab0",
"sha256": "18adbc1nnj94qhggpcxmpd5i1rz0zx93cpphl09mw4c7s65rzah7"
"private": false,
"rev": "9f13d8d8a2233aae54e15c39acf68181893b859a",
"sha256": "1vhbw9bdqpfddavfjfdrq6kk5wwsd8hbgb8pnna9i2db3x3cmzvy"
}
```

Expand All @@ -96,5 +97,5 @@ You can use `nix-shell` to enter a shell from which you can build any of the sub
Because of the inter-related nature of these packages, `rhyolite-test-suite` tests that all of them can be built against one another. To test, run:

```bash
nix-shell --run cabal build test
nix-shell --run "cabal build test"
```
2 changes: 2 additions & 0 deletions account/backend/rhyolite-account-backend.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ category: Web

library
exposed-modules: Rhyolite.Backend.Account
Rhyolite.Backend.Account.Db
Rhyolite.Backend.Account.Notify
build-depends: base
, aeson
, beam-core
Expand Down
202 changes: 3 additions & 199 deletions account/backend/src/Rhyolite/Backend/Account.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ Description:
{-# Language OverloadedStrings #-}
module Rhyolite.Backend.Account
( createAccount
, login
, ensureAccountExists
, ensureAccountExistsNoNotify
, login
, setAccountPassword
, setAccountPasswordHash
, makePasswordHash
Expand All @@ -20,200 +19,5 @@ module Rhyolite.Backend.Account
, resetPasswordHash
) where

import Control.Monad (guard)
import Control.Monad.Trans.Maybe
import Crypto.PasswordStore
import Data.Aeson
import Data.ByteString
import Data.Constraint.Extras
import Data.Constraint.Forall
import Data.Functor.Identity
import Data.Maybe
import Data.Text
import Data.Text.Encoding (encodeUtf8)
import qualified Data.Text.Encoding as T
import Data.Time
import Database.Beam
import Database.Beam.Backend.SQL.BeamExtensions
import Database.Beam.Postgres
import Data.Signed
import Data.Signed.ClientSession
import Database.Beam.Postgres.Full hiding (insert)
import Database.Beam.Postgres.Syntax
import Database.PostgreSQL.Simple.Beam ()
import Rhyolite.Account
import Rhyolite.DB.Beam (current_timestamp_)
import Rhyolite.DB.NotifyListen
import Rhyolite.DB.NotifyListen.Beam
import Web.ClientSession as CS

-- | Creates a new account and emits a db notification about it
createAccount
:: (Has' ToJSON notice Identity, ForallF ToJSON notice)
=> DatabaseEntity Postgres db (TableEntity Account)
-> notice (PrimaryKey Account Identity)
-> Text
-> Text
-> Pg (Either Text (PrimaryKey Account Identity))
createAccount accountTable noticeWrapper email pass = do
hash <- makePasswordHash pass
accountIds <- runPgInsertReturningList $ flip returning _account_id $ insert accountTable $ insertExpressions
[ Account
{ _account_id = default_
, _account_email = lower_ (val_ email)
, _account_password = val_ (Just hash)
, _account_passwordResetNonce = just_ current_timestamp_
}
]
case accountIds of
[accountId] -> do
notify NotificationType_Insert noticeWrapper (AccountId accountId)
pure $ Right $ AccountId accountId
_ -> pure $ Left "Failed to create account"

-- | Attempts to login a user given some credentials.
login
:: Database Postgres db
=> DatabaseEntity Postgres db (TableEntity Account)
-> Text
-> Text
-> Pg (Maybe (PrimaryKey Account Identity))
login accountTable email pass = runMaybeT $ do
(aid, mPwHash) <- MaybeT $ fmap listToMaybe $ runSelectReturningList $ select $ do
acc <- all_ accountTable
guard_ $ lower_ (_account_email acc) ==. lower_ (val_ email)
pure (_account_id acc, _account_password acc)
pwHash <- MaybeT $ pure mPwHash
guard $ verifyPasswordWith pbkdf2 (2^) (T.encodeUtf8 pass) pwHash
pure (AccountId aid)

ensureAccountExistsNoNotify
:: (Database Postgres db)
=> DatabaseEntity Postgres db (TableEntity Account)
-> Text
-> Pg (Bool, PrimaryKey Account Identity)
ensureAccountExistsNoNotify accountTable email = do
existingAccountId <- runSelectReturningOne $ select $ fmap primaryKey $ filter_ (\x ->
lower_ (_account_email x) ==. lower_ (val_ email)) $ all_ accountTable
case existingAccountId of
Just existing -> return (False, existing)
Nothing -> do
results <- runInsertReturningList $ insert accountTable $ insertExpressions
[ Account
{ _account_id = default_
, _account_email = lower_ (val_ email)
, _account_password = nothing_
, _account_passwordResetNonce = nothing_
}
]
case results of
[acc] -> do
let aid = primaryKey acc
-- notify NotificationType_Insert (notification accountTable) aid
pure (True, aid)
_ -> error "ensureAccountExists: Creating account failed"

ensureAccountExists
:: (Database Postgres db, HasNotification n Account, Has' ToJSON n Identity, ForallF ToJSON n)
=> DatabaseEntity Postgres db (TableEntity Account)
-> Text
-> Pg (Bool, PrimaryKey Account Identity)
ensureAccountExists accountTable email = do
existingAccountId <- runSelectReturningOne $ select $ fmap primaryKey $ filter_ (\x ->
lower_ (_account_email x) ==. lower_ (val_ email)) $ all_ accountTable
case existingAccountId of
Just existing -> return (False, existing)
Nothing -> do
results <- runInsertReturningList $ insert accountTable $ insertExpressions
[ Account
{ _account_id = default_
, _account_email = lower_ (val_ email)
, _account_password = nothing_
, _account_passwordResetNonce = nothing_
}
]
case results of
[acc] -> do
let aid = primaryKey acc
notify NotificationType_Insert (notification accountTable) aid
pure (True, aid)
_ -> error "ensureAccountExists: Creating account failed"

setAccountPassword
:: DatabaseEntity Postgres db (TableEntity Account)
-> PrimaryKey Account Identity
-> Text
-> Pg ()
setAccountPassword tbl aid password = do
pw <- liftIO $ makePasswordHash password
setAccountPasswordHash tbl aid pw

setAccountPasswordHash
:: DatabaseEntity Postgres db (TableEntity Account)
-> PrimaryKey Account Identity
-> ByteString
-> Pg ()
setAccountPasswordHash accountTable aid hash = runUpdate $ update accountTable
(\x -> mconcat
[ _account_password x <-. val_ (Just hash)
, _account_passwordResetNonce x <-. nothing_
]
)
(\x -> primaryKey x ==. val_ aid)

makePasswordHash
:: MonadIO m
=> Text
-> m ByteString
makePasswordHash pw = do
salt <- liftIO genSaltIO
return $ makePasswordSaltWith pbkdf2 (2^) (encodeUtf8 pw) salt 14

resetPassword
:: (Database Postgres db)
=> DatabaseEntity Postgres db (TableEntity Account)
-> PrimaryKey Account Identity
-> UTCTime
-> Text
-> Pg (Maybe (PrimaryKey Account Identity))
resetPassword tbl aid t pw = do
hash <- makePasswordHash pw
resetPasswordHash tbl aid t hash

resetPasswordHash
:: (Database Postgres db)
=> DatabaseEntity Postgres db (TableEntity Account)
-> PrimaryKey Account Identity
-> UTCTime
-> ByteString
-> Pg (Maybe (PrimaryKey Account Identity))
resetPasswordHash accountTable aid nonce pwhash = do
macc <- runSelectReturningOne $ lookup_ accountTable aid
case macc of
Nothing -> return Nothing
Just a -> if _account_passwordResetNonce a == Just nonce
then do
setAccountPasswordHash accountTable aid pwhash
return $ Just aid
else fail "nonce mismatch"

passwordResetToken
:: MonadIO m
=> CS.Key
-> PrimaryKey Account Identity
-> UTCTime
-> m (Signed PasswordResetToken)
passwordResetToken csk aid nonce = do
liftIO $ signWithKey csk $ PasswordResetToken (aid, nonce)

newNonce
:: DatabaseEntity Postgres db (TableEntity Account)
-> PrimaryKey Account Identity
-> Pg (Maybe UTCTime)
newNonce accountTable aid = do
a <- runUpdateReturningList $ update accountTable
(\x -> _account_passwordResetNonce x <-. just_ current_timestamp_)
(\x -> primaryKey x ==. val_ aid)
pure $ case a of
[acc] -> _account_passwordResetNonce acc
_ -> Nothing
import Rhyolite.Backend.Account.Db hiding (createAccount, ensureAccountExists)
import Rhyolite.Backend.Account.Notify (createAccount, ensureAccountExists)
Loading