17 Commits

Author SHA1 Message Date
Râu Cao
f08bb56a7a 0.5.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-01 11:44:25 +02:00
c1f275463e Merge pull request 'Add Redis, Sidekiq to Docker Compose setup' (#110) from feature/docker-compose_sidekiq into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #110
Reviewed-by: galfert <garret.alfert@gmail.com>
2023-03-31 09:09:46 +00:00
324809f77e Merge pull request 'Expire inactive sessions, optionally allow to stay signed in' (#82) from feature/8-session_timeouts into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #82
Reviewed-by: galfert <garret.alfert@gmail.com>
2023-03-31 07:58:24 +00:00
Râu Cao
f9b07bcb01 Use development branch of release drafter action
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-28 17:27:31 +02:00
Râu Cao
986eb5387c Use release drafter fork with PR ID fix
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-28 17:13:39 +02:00
f76e2c2f14 Merge pull request 'Add Gitea Release Drafter as Gitea Action' (#111) from feature/release_drafter into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #111
2023-03-28 14:21:44 +00:00
Râu Cao
22a7bbe6eb Add Gitea Release Drafter as Gitea Action
All checks were successful
Update release notes draft
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-03-28 16:17:19 +02:00
18f4deb30f Merge pull request 'Add (optional) Sentry integration' (#108) from feature/sentry_integration into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #108
Reviewed-by: greg <greg@noreply.kosmos.org>
2023-03-28 12:53:00 +00:00
Râu Cao
9f9bf6fd80 Add Redis and Sidekiq to Docker Compose setup
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Update release notes draft
2023-03-28 12:24:58 +02:00
Râu Cao
d2987da70a Send Devise emails via Sidekiq 2023-03-28 12:22:17 +02:00
Râu Cao
6b7a80e23a Make Redis URL configurable 2023-03-28 12:21:54 +02:00
Râu Cao
42b9b27561 Allow external network access
All checks were successful
continuous-integration/drone/push Build is passing
Useful for connecting to services on private networks for example.
2023-03-28 11:38:56 +02:00
Râu Cao
c17c980b69 Prepare for multiple akkounts containers
All checks were successful
continuous-integration/drone/push Build is passing
Initially "web" and "sidekiq"
2023-03-28 11:25:10 +02:00
Râu Cao
f199d5d12a Add (optional) Sentry integration
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
A Sentry DSN can be set via `SENTRY_DSN` and authenticated users will be
tagged with ID and username (cn) in events.
2023-03-27 12:47:28 +02:00
Râu Cao
a8a8fba14c Change styling of Devise shared links
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Update release notes draft
2023-03-19 18:07:09 +07:00
Râu Cao
8a7016a30b Add remember-me function for sign-in
When checked, remember user for 2 weeks. Otherwise expire session after
30 minutes.
2023-03-19 18:06:18 +07:00
Râu Cao
e2618de7c6 Add time limit for inactive sessions
closes #8
2023-03-19 16:16:36 +07:00
35 changed files with 220 additions and 374 deletions

View File

@@ -7,6 +7,8 @@ SMTP_DOMAIN=example.com
SMTP_AUTH_METHOD=plain
SMTP_ENABLE_STARTTLS=auto
REDIS_URL='redis://localhost:6379/1'
LDAP_HOST=localhost
LDAP_PORT=389
LDAP_ADMIN_PASSWORD=passthebutter
@@ -18,7 +20,6 @@ DISCOURSE_PUBLIC_URL='https://community.kosmos.org'
GITEA_PUBLIC_URL='https://gitea.kosmos.org'
MASTODON_PUBLIC_URL='https://kosmos.social'
MEDIAWIKI_PUBLIC_URL='https://wiki.kosmos.org'
RS_STORAGE_URL='https://storage.kosmos.org'
EJABBERD_ADMIN_URL='https://xmpp.kosmos.org/admin'
EJABBERD_API_URL='https://xmpp.kosmos.org/api'

View File

@@ -0,0 +1,13 @@
name-template: 'v$RESOLVED_VERSION'
tag-template: 'v$RESOLVED_VERSION'
version-resolver:
major:
labels:
- 'release/major'
minor:
labels:
- 'release/minor'
patch:
labels:
- 'release/patch'
default: patch

View File

@@ -0,0 +1,11 @@
name: Release Drafter
on:
pull_request:
types: [closed]
jobs:
release_drafter_job:
name: Update release notes draft
runs-on: ubuntu-latest
steps:
- name: Release Drafter
uses: https://github.com/raucao/gitea-release-drafter@dev

View File

@@ -1,8 +1,13 @@
# syntax=docker/dockerfile:1
FROM ruby:2.7.6
RUN apt-get update -qq && apt-get install -y curl ldap-utils
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN apt-get update -qq && apt-get install -y --no-install-recommends curl \
ldap-utils tini
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
RUN apt-get update && apt-get install -y nodejs
WORKDIR /akkounts
COPY Gemfile /akkounts/Gemfile
COPY Gemfile.lock /akkounts/Gemfile.lock
@@ -12,11 +17,5 @@ RUN gem install foreman
RUN npm install -g yarn
RUN yarn install
# Add a script to be executed every time the container starts.
COPY docker/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
ENTRYPOINT ["/usr/bin/tini", "--"]
EXPOSE 3000
# Configure the main process to run when running the image
CMD ["bin", "dev"]

View File

@@ -48,6 +48,10 @@ gem 'faraday'
gem 'sidekiq', '< 7'
gem 'sidekiq-scheduler'
# Monitoring
gem "sentry-ruby"
gem "sentry-rails"
group :development, :test do
# Use sqlite3 as the database for Active Record
gem 'sqlite3', '~> 1.4'

View File

@@ -254,6 +254,11 @@ GEM
ruby2_keywords (0.0.5)
rufus-scheduler (3.8.2)
fugit (~> 1.1, >= 1.1.6)
sentry-rails (5.8.0)
railties (>= 5.0)
sentry-ruby (~> 5.8.0)
sentry-ruby (5.8.0)
concurrent-ruby (~> 1.0, >= 1.0.2)
sidekiq (6.5.5)
connection_pool (>= 2.2.2)
rack (~> 2.0)
@@ -335,6 +340,8 @@ DEPENDENCIES
rails-settings-cached (~> 2.8.3)
rqrcode (~> 2.0)
rspec-rails
sentry-rails
sentry-ruby
sidekiq (< 7)
sidekiq-scheduler
sprockets-rails

View File

@@ -14,7 +14,7 @@ so:
1. Make sure [Docker Compose is installed][1] and Docker is running (included in
Docker Desktop)
2. Uncomment the `web` section in `docker-compose.yml`
2. Uncomment the `redis`, `web`, and `sidekiq` sections in `docker-compose.yml`
3. Run `docker compose up` and wait until 389ds announces its successful start
in the log output
4. `docker-compose exec ldap dsconf localhost backend create --suffix="dc=kosmos,dc=org" --be-name="dev"`

View File

@@ -5,10 +5,4 @@
&:visited { @apply text-indigo-600; }
&:active { @apply text-red-600; }
}
.devise-links {
a {
@apply ks-text-link;
}
}
}

View File

@@ -1,6 +1,6 @@
<%= button_tag type: "button", name: "toggle", data: @data,
role: "switch", aria: { checked: @enabled.to_s },
disabled: !@input_enabled,
tabindex: @tabindex, disabled: !@input_enabled,
class: "#{ @enabled ? 'bg-blue-600' : 'bg-gray-200' }
#{ @class_names.present? ? @class_names : '' }
relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer

View File

@@ -2,11 +2,12 @@
module FormElements
class ToggleComponent < ViewComponent::Base
def initialize(enabled:, input_enabled: true, data: nil, class_names: nil)
def initialize(enabled:, input_enabled: true, data: nil, class_names: nil, tabindex: nil)
@enabled = !!enabled
@input_enabled = input_enabled
@data = data
@class_names = class_names
@tabindex = tabindex
end
end
end

View File

@@ -3,6 +3,18 @@ class ApplicationController < ActionController::Base
render :text => exception, :status => 500
end
before_action :sentry_set_user
def sentry_set_user
return unless Setting.sentry_enabled
if user_signed_in?
Sentry.set_user(id: current_user.id, username: current_user.cn)
else
Sentry.set_user({})
end
end
def require_user_signed_in
unless user_signed_in?
redirect_to welcome_path and return

View File

@@ -1,130 +0,0 @@
class Rs::OauthController < ApplicationController
before_action :require_user_signed_in
def new
username, org = params[:useraddress].split("@")
@user = User.where(cn: username.downcase, ou: org).first
@scopes = parse_scopes params[:scope]
@redirect_uri = params[:redirect_uri]
@client_id = params[:client_id]
@state = params[:state]
@root_access_requested = (@scopes & [":r",":rw"]).any?
@denial_url = url_with_state("#{@redirect_uri}#error=access_denied", @state)
@expire_at_dates = [["Never", nil],
["In 1 month", 1.month.from_now],
["In 1 day", 1.day.from_now]]
http_status :bad_request and return unless @redirect_uri.present?
unless current_user == @user
sign_out :user
redirect_to new_rs_oauth_url(@user.address,
scope: params[:scope],
redirect_uri: params[:redirect_uri],
client_id: params[:client_id],
state: params[:state])
return
end
unless @client_id.present?
redirect_to url_with_state("#{@redirect_uri}#error=invalid_request", @state) and return
end
if @scopes.empty?
redirect_to url_with_state("#{@redirect_uri}#error=invalid_scope", @state) and return
end
unless hostname_of(@client_id) == hostname_of(@redirect_uri)
redirect_to url_with_state("#{@redirect_uri}#error=invalid_client", @state) and return
end
@client_id.gsub!(/http(s)?:\/\//, "")
# TODO
# if auth = current_user.remote_storage_authorizations.valid.where(permissions: @scopes, client_id: @client_id).first
# redirect_to url_with_state("#{@redirect_uri}#access_token=#{auth.token}", @state), allow_other_host: true
# end
end
def create
unless current_user.id.to_s == params[:user_id]
Rails.logger.info("NO MATCH: #{params[:user_id]}, #{current_user.id}")
http_status :forbidden and return
end
permissions = parse_scopes params[:scope]
redirect_uri = params[:redirect_uri].presence
client_id = params[:client_id].presence
state = params[:state].presence
expire_at = params[:expire_at].presence
http_status :bad_request and return unless redirect_uri.present?
if permissions.empty?
redirect_to url_with_state("#{redirect_uri}#error=invalid_scope", state), allow_other_host: true and return
end
unless client_id.present?
redirect_to url_with_state("#{redirect_uri}#error=invalid_request", state), allow_other_host: true and return
end
unless hostname_of(client_id) == hostname_of(redirect_uri)
redirect_to url_with_state("#{redirect_uri}#error=invalid_client", state), allow_other_host: true and return
end
client_id.gsub!(/http(s)?:\/\//, "")
auth = current_user.remote_storage_authorizations.create!(
permissions: permissions,
client_id: client_id,
redirect_uri: redirect_uri,
app_name: client_id, #TODO use user-defined name
expire_at: expire_at
)
redirect_to url_with_state("#{redirect_uri}#access_token=#{auth.token}", state), allow_other_host: true
end
# GET /rs/oauth/token/:id/launch_app
def launch_app
auth = current_user.remote_storage_authorizations.find(params[:id])
redirect_to app_auth_url(auth)
end
private
def app_auth_url(auth)
url = "#{auth.url}#remotestorage=#{current_user.address}"
url += "&access_token=#{auth.token}"
url
end
def hostname_of(uri)
uri.gsub(/http(s)?:\/\//, "").split(":")[0].split("/")[0]
end
def parse_scopes(scope_string)
return [] if scope_string.blank?
scopes = scope_string.
gsub(/\[|\]/, "").
gsub(/\,/, " ").
gsub(/\/:/, ":").
split(/\s/).map(&:strip).
reject(&:empty?)
scopes = [":r"] if scopes.include?("*:r")
scopes = [":rw"] if scopes.include?("*:rw")
scopes
end
def url_with_state(url, state)
state ? "#{url}&state=#{CGI.escape(state)}" : url
end
end

View File

@@ -1,11 +0,0 @@
module OauthHelper
def scope_name(scope)
scope.gsub(/(\:.+)/, '')
end
def scope_permissions(scope)
scope.match(/\:r$/) ? "r" : "rw"
end
end

View File

@@ -4,6 +4,10 @@ export default class extends Controller {
static targets = ["buttons", "countdown"]
connect() {
// Devise timeoutable ends up adding a second flash message without content
// TODO investigate bug
if (this.element.textContent.trim() == "true") return;
const timeoutSeconds = parseInt(this.data.get("timeout"));
setTimeout(() => {

View File

@@ -1,32 +0,0 @@
class RemoteStorageAuthorization < ApplicationRecord
belongs_to :user
serialize :permissions
validates_presence_of :permissions
validates_presence_of :client_id
scope :valid, -> { where(expire_at: nil).or(where(expire_at: (DateTime.now)..)) }
scope :expired, -> { where(expire_at: ..(DateTime.now)) }
after_initialize do |a|
a.permisisons = [] if a.permissions == nil
end
before_create :generate_token
def url
if self.redirect_uri
uri = URI.parse self.redirect_uri
"#{uri.scheme}://#{client_id}"
else
"http://#{client_id}"
end
end
private
def generate_token(length=16)
self.token = SecureRandom.hex(length) if self.token.blank?
end
end

View File

@@ -2,6 +2,13 @@
class Setting < RailsSettings::Base
cache_prefix { "v1" }
#
# Internal services
#
field :redis_url, type: :string, readonly: true,
default: ENV["REDIS_URL"] || "redis://localhost:6379/0"
#
# Registrations
#
@@ -10,6 +17,13 @@ class Setting < RailsSettings::Base
account accounts donations mail webmaster support
]
#
# Sentry
#
field :sentry_enabled, type: :boolean, readonly: true,
default: (ENV["SENTRY_DSN"].present?.to_s || false)
#
# Discourse
#
@@ -90,14 +104,4 @@ class Setting < RailsSettings::Base
#
field :nostr_enabled, type: :boolean, default: true
#
# RemoteStorage
#
field :remotestorage_enabled, type: :boolean,
default: (ENV["RS_STORAGE_URL"].present?.to_s || false)
field :rs_storage_url, type: :string,
default: ENV["RS_STORAGE_URL"].presence
end

View File

@@ -14,8 +14,6 @@ class User < ApplicationRecord
has_many :accounts, through: :lndhub_user
has_many :remote_storage_authorizations
validates_uniqueness_of :cn
validates_length_of :cn, :minimum => 3
validates_format_of :cn, with: /\A([a-z0-9\-])*\z/,
@@ -40,7 +38,9 @@ class User < ApplicationRecord
devise :ldap_authenticatable,
:confirmable,
:recoverable,
:validatable
:validatable,
:timeoutable,
:rememberable
def ldap_before_save
self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first
@@ -65,6 +65,10 @@ class User < ApplicationRecord
end
end
def send_devise_notification(notification, *args)
devise_mailer.send(notification, self, *args).deliver_later
end
def reset_password(new_password, new_password_confirmation)
self.password = new_password
self.password_confirmation = new_password_confirmation

View File

@@ -1,17 +0,0 @@
<h3>RemoteStorage</h3>
<ul role="list">
<%= render FormElements::FieldsetToggleComponent.new(
form: f,
attribute: :remotestorage_enabled,
enabled: Setting.remotestorage_enabled?,
title: "Enable RemoteStorage integration",
description: "RemoteStorage configuration present and features enabled"
) %>
<% if Setting.remotestorage_enabled? %>
<%= render FormElements::FieldsetComponent.new(title: "Storage URL") do %>
<%= f.text_field :rs_storage_url,
value: Setting.rs_storage_url,
class: "w-full", disabled: true %>
<% end %>
<% end %>
</ul>

View File

@@ -7,19 +7,43 @@
<%= f.label :cn, 'User', class: 'block mb-2 font-bold' %>
<p class="flex gap-2 items-center">
<%= f.text_field :cn, autofocus: true, autocomplete: "username",
required: true, class: "relative grow"%>
required: true, class: "relative grow", tabindex: "1" %>
<span class="relative shrink-0 text-gray-500">@ kosmos.org</span>
</p>
</div>
<p>
<p class="mb-8">
<%= f.label :password, class: 'block mb-2 font-bold' %>
<%= f.password_field :password, autocomplete: "current-password",
required: true, class: "w-full"%>
required: true, class: "w-full", tabindex: "2" %>
</p>
<p class="mt-8">
<%= f.submit "Log in", class: 'btn-md btn-blue w-full' %>
<%= tag.div class: "flex items-center mb-8 gap-x-3", data: {
controller: "settings--toggle",
:'settings--toggle-switch-enabled-value' => "false"
} do %>
<div class="relative inline-flex flex-shrink-0">
<%= render FormElements::ToggleComponent.new(
enabled: false, input_enabled: true, class_names: "hidden",
tabindex: "3", data: {
:'settings--toggle-target' => "button",
action: "settings--toggle#toggleSwitch"
}) %>
<%= f.check_box :remember_me, {
checked: false,
data: { :'settings--toggle-target' => "checkbox" }
}, "true", "false" %>
</div>
<%= f.label :remember_me,
class: "text-gray-500 flex flex-col",
data: { action: "click->settings--toggle#toggleSwitch" } %>
<p class="grow text-sm text-right">
<%= link_to "Forgot your password?", new_password_path(resource_name),
class: "text-gray-500 underline" %><br />
</p>
<% end %>
<p>
<%= f.submit "Log in", class: 'btn-md btn-blue w-full', tabindex: "4" %>
</p>
<% end %>
<%= render "devise/shared/links" %>
<% end %>

View File

@@ -1,25 +1,29 @@
<div class="devise-links mt-8 text-sm">
<%- if controller_name != 'sessions' %>
<p class="mb-1.5">
<%= link_to "Log in", new_session_path(resource_name) %><br />
<p class="mb-2">
<%= link_to "Log in", new_session_path(resource_name),
class: "text-gray-500 underline" %>
</p>
<% end %>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<p class="mb-1.5">
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
<p class="mb-2">
<%= link_to "Forgot your password?", new_password_path(resource_name),
class: "text-gray-500 underline" %>
</p>
<% end %>
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<p class="mb-1.5">
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
<%- if devise_mapping.confirmable? && !controller_name.match(/^(confirmations|sessions)$/) %>
<p class="mb-2">
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name),
class: "text-gray-500 underline" %>
</p>
<% end %>
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
<p class="mb-1.5">
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
<p class="mb-2">
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name),
class: "text-gray-500 underline" %>
</p>
<% end %>
</div>

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 512 512" fill="currentColor" stroke="currentColor" stroke-width="2" class="<%= custom_class %>"><path d="M475.31 364.144L288 256l187.31-108.144c5.74-3.314 7.706-10.653 4.392-16.392l-4-6.928c-3.314-5.74-10.653-7.706-16.392-4.392L272 228.287V12c0-6.627-5.373-12-12-12h-8c-6.627 0-12 5.373-12 12v216.287L52.69 120.144c-5.74-3.314-13.079-1.347-16.392 4.392l-4 6.928c-3.314 5.74-1.347 13.079 4.392 16.392L224 256 36.69 364.144c-5.74 3.314-7.706 10.653-4.392 16.392l4 6.928c3.314 5.74 10.653 7.706 16.392 4.392L240 283.713V500c0 6.627 5.373 12 12 12h8c6.627 0 12-5.373 12-12V283.713l187.31 108.143c5.74 3.314 13.079 1.347 16.392-4.392l4-6.928c3.314-5.74 1.347-13.079-4.392-16.392z"/></svg>

Before

Width:  |  Height:  |  Size: 760 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-folder <%= custom_class %>"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-folder"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 311 B

View File

@@ -1,60 +0,0 @@
<%= render HeaderComponent.new(title: "App Authorization") %>
<%= render MainSimpleComponent.new do %>
<section class="px-16 pb-8 mt-8">
<p class="text-lg mb-8">
The app
<%= link_to @client_id, "https://#{@client_id}", class: "ks-text-link" %>
is asking for access to these folders:
</p>
<p class="text-xl mb-8">
<% if @root_access_requested %>
<span class="text-red-700">
<span class="text-red-700">
<%= render partial: "icons/asterisk", locals: { custom_class: "inline-block align-middle mb-1" } %>
All files and directories
</span>
<% if (@scopes & [":r"]).any? %>
<span class="text-sm text-gray-500">(read only)</span>
<% end %>
</span>
<% else %>
<% @scopes.each do |scope| %>
<span class="text-gray-500">
<span>
<%= render partial: "icons/folder", locals: { custom_class: "inline-block align-middle mb-2" } %>
<%= scope_name(scope) %>
</span>
<% if scope_permissions(scope) == "r" %>
<span class="text-sm text-gray-500">(read only)</span>
<% end %>
</span>
<% end %>
<% end %>
</p>
<%= form_with(url: rs_oauth_path, method: :post, data: { turbo: false }) do |f| %>
<%= f.hidden_field :redirect_uri, value: @redirect_uri %>
<%= f.hidden_field :scope, value: @scopes.join(" ") %>
<%= f.hidden_field :user_id, value: @user.id %>
<%= f.hidden_field :client_id, value: @client_id %>
<%= f.hidden_field :state, value: @state %>
<p>
<%= f.label :expire_at, "Expire:" %>
<%= f.select :expire_at, options_for_select(@expire_at_dates) %>
</p>
<p class="text-sm text-gray-500 my-10">
You can revoke access for this app at any time on your storage dashboard.
</p>
<div>
<%= f.submit class: "btn-md btn-blue w-full sm:w-auto", data: { disable_with: "Saving..." } do %>
Allow
<% end %>
<%= link_to @denial_url, class: "btn-md btn-red w-full sm:w-auto" do %>
Deny
<% end %>
</div>
<% end %>
</section>
<% end %>

View File

@@ -47,10 +47,3 @@
icon: Setting.nostr_enabled? ? "check" : "x",
active: current_page?(admin_settings_services_path(params: { s: "nostr" })),
) %>
<%= render SidenavLinkComponent.new(
level: 2,
name: "RemoteStorage",
path: admin_settings_services_path(params: { s: "remotestorage" }),
icon: Setting.remotestorage_enabled? ? "check" : "x",
active: current_page?(admin_settings_services_path(params: { s: "remotestorage" })),
) %>

View File

@@ -186,13 +186,13 @@ Devise.setup do |config|
# ==> Configuration for :rememberable
# The time the user will be remembered without asking for credentials again.
# config.remember_for = 2.weeks
config.remember_for = 2.weeks
# Invalidates all the remember me tokens when the user signs out.
config.expire_all_remember_me_on_sign_out = true
# If true, extends the user's remember period when remembered via cookie.
# config.extend_remember_period = false
config.extend_remember_period = true
# Options to be passed to the created cookie. For instance, you can set
# secure: true in order to force SSL only cookies.
@@ -210,7 +210,7 @@ Devise.setup do |config|
# ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this
# time the user will be asked for credentials again. Default is 30 minutes.
# config.timeout_in = 30.minutes
config.timeout_in = 30.minutes
# ==> Configuration for :lockable
# Defines which strategy will be used to lock an account.

View File

@@ -0,0 +1,9 @@
if ENV["SENTRY_DSN"].present?
Sentry.init do |config|
config.dsn = ENV["SENTRY_DSN"]
config.breadcrumbs_logger = [:active_support_logger, :http_logger]
config.traces_sampler = lambda do |context|
true
end
end
end

View File

@@ -0,0 +1,5 @@
require_relative "../../app/models/setting"
Sidekiq.configure_server do |config|
config.redis = { url: Setting.redis_url }
end

View File

@@ -1,7 +1,7 @@
require 'sidekiq/web'
Rails.application.routes.draw do
devise_for :users, :controllers => { :confirmations => "users/confirmations" }
devise_for :users, controllers: { confirmations: "users/confirmations" }
get 'welcome', to: 'welcome#index'
get 'check_your_email', to: 'welcome#check_your_email'
@@ -54,12 +54,6 @@ Rails.application.routes.draw do
end
end
namespace :rs do
resource :oauth, only: [:new, :create], path_names: { new: ':useraddress' },
controller: 'oauth', constraints: { useraddress: /[^\/]+/}
get 'oauth/token/:id/launch_app' => 'oauth#launch_app', as: :launch_app
end
authenticate :user, ->(user) { user.is_admin? } do
mount Sidekiq::Web => '/sidekiq'
end

View File

@@ -1,3 +1,4 @@
:concurrency: 2
:queues:
- default
- mailers

View File

@@ -1,17 +0,0 @@
class CreateRemoteStorageAuthorizations < ActiveRecord::Migration[7.0]
def change
create_table :remote_storage_authorizations do |t|
t.references :user, null: false, foreign_key: true
t.string :token
t.text :permissions, array: true, default: [].to_yaml
t.string :client_id
t.string :redirect_uri
t.string :app_name
t.datetime :expire_at
t.timestamps
end
add_index :remote_storage_authorizations, :permissions, using: 'gin'
end
end

View File

@@ -0,0 +1,6 @@
class AddRememberCreatedAtToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :remember_created_at, :datetime
add_column :users, :remember_token, :string
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_03_12_212030) do
ActiveRecord::Schema[7.0].define(version: 2023_03_19_101128) do
create_table "donations", force: :cascade do |t|
t.integer "user_id"
t.integer "amount_sats"
@@ -34,20 +34,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_12_212030) do
t.index ["user_id"], name: "index_invitations_on_user_id"
end
create_table "remote_storage_authorizations", force: :cascade do |t|
t.integer "user_id", null: false
t.string "token"
t.text "permissions", default: "--- []\n"
t.string "client_id"
t.string "redirect_uri"
t.string "app_name"
t.datetime "expire_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["permissions"], name: "index_remote_storage_authorizations_on_permissions"
t.index ["user_id"], name: "index_remote_storage_authorizations_on_user_id"
end
create_table "settings", force: :cascade do |t|
t.string "var", null: false
t.text "value"
@@ -71,9 +57,10 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_12_212030) do
t.text "ln_login_ciphertext"
t.text "ln_password_ciphertext"
t.string "ln_account"
t.datetime "remember_created_at"
t.string "remember_token"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "remote_storage_authorizations", "users"
end

View File

@@ -3,11 +3,67 @@ services:
image: 4teamwork/389ds:latest
volumes:
- ./tmp/389ds:/data
networks:
- external_network
- internal_network
ports:
- "389:3389"
environment:
DS_DM_PASSWORD: passthebutter
SUFFIX_NAME: "dc=kosmos,dc=org"
# redis:
# restart: always
# image: redis:7-alpine
# networks:
# - internal_network
# healthcheck:
# test: ['CMD', 'redis-cli', 'ping']
# volumes:
# - ./tmp/redis:/data
# web:
# build: .
# tty: true
# command: bash -c "rm -f /akkounts/tmp/pids/server.pid; bin/dev"
# volumes:
# - .:/akkounts
# networks:
# - external_network
# - internal_network
# ports:
# - "3000:3000"
# environment:
# RAILS_ENV: development
# REDIS_URL: redis://redis:6379/0
# LDAP_HOST: ldap
# LDAP_PORT: 3389
# LDAP_ADMIN_PASSWORD: passthebutter
# LDAP_USE_TLS: "false"
# depends_on:
# - ldap
# - redis
# sidekiq:
# build: .
# command: bash -c "bundle exec sidekiq -C config/sidekiq.yml"
# volumes:
# - .:/akkounts
# networks:
# - internal_network
# environment:
# RAILS_ENV: development
# REDIS_URL: redis://redis:6379/0
# LDAP_HOST: ldap
# LDAP_PORT: 3389
# LDAP_ADMIN_PASSWORD: passthebutter
# LDAP_USE_TLS: "false"
# LAUNCHY_DRY_RUN: true
# BROWSER: /dev/null
# depends_on:
# - ldap
# - redis
# phpldapadmin:
# image: osixia/phpldapadmin:0.9.0
# ports:
@@ -16,19 +72,8 @@ services:
# PHPLDAPADMIN_HTTPS: false
# PHPLDAPADMIN_LDAP_HOSTS: "#PYTHON2BASH:[{'ldap': [{'server': [{'tls': False}, {'port': 3389}]}, {'login': [{'bind_id': 'cn=Directory Manager'}, {'bind_pass': 'passthebutter'}]}]}]"
# PHPLDAPADMIN_LDAP_CLIENT_TLS: false
# web:
# build: .
# tty: true
# command: bash -c "sleep 5 && rm -f tmp/pids/server.pid && bin/dev"
# volumes:
# - .:/akkounts
# ports:
# - "3000:3000"
# environment:
# RAILS_ENV: development
# LDAP_HOST: ldap
# LDAP_PORT: 3389
# LDAP_ADMIN_PASSWORD: passthebutter
# LDAP_USE_TLS: "false"
# depends_on:
# - ldap
networks:
external_network:
internal_network:
internal: true

View File

@@ -1,8 +0,0 @@
#!/bin/bash
set -e
# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid
# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

View File

@@ -11,7 +11,7 @@
"postcss-preset-env": "^7.8.3",
"tailwindcss": "^3.2.4"
},
"version": "0.4.0",
"version": "0.5.0",
"scripts": {
"build:css:tailwind": "tailwindcss --postcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css",
"build:css": "yarn run build:css:tailwind"