diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 2081e7a..7544f55 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -18,6 +18,8 @@ class Admin::UsersController < Admin::BaseController if Setting.lndhub_admin_enabled? @lndhub_user = @user.lndhub_user end + + @services_enabled = @user.services_enabled end private diff --git a/app/controllers/users/confirmations_controller.rb b/app/controllers/users/confirmations_controller.rb new file mode 100644 index 0000000..340b538 --- /dev/null +++ b/app/controllers/users/confirmations_controller.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class Users::ConfirmationsController < Devise::ConfirmationsController + # GET /resource/confirmation?confirmation_token=abcdef + def show + self.resource = resource_class.confirm_by_token(params[:confirmation_token]) + yield resource if block_given? + + if resource.errors.empty? + set_flash_message!(:success, :confirmed) + resource.devise_after_confirmation + respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) } + else + respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new } + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 3c4be54..31599c4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -42,9 +42,9 @@ class User < ApplicationRecord def ldap_before_save self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first - - dn = Devise::LDAP::Adapter.get_ldap_param(self.cn, "dn") - self.ou = dn.split(',').select{|e| e[0..1] == "ou"}.first.delete_prefix("ou=") + self.ou = dn.split(',') + .select{|e| e[0..1] == "ou"}.first + .delete_prefix("ou=") if self.confirmed_at.blank? && self.confirmation_token.blank? # User had an account with a trusted email address before akkounts was a thing @@ -52,6 +52,10 @@ class User < ApplicationRecord end end + def devise_after_confirmation + enable_service %w[discourse gitea wiki xmpp] + end + def reset_password(new_password, new_password_confirmation) self.password = new_password self.password_confirmation = new_password_confirmation @@ -70,12 +74,6 @@ class User < ApplicationRecord end end - def ldap_entry - return @ldap_entry if defined?(@ldap_entry) - ldap = LdapService.new - @ldap_entry = ldap.fetch_users(uid: self.cn, ou: self.ou).first - end - def address "#{self.cn}@#{self.ou}" end @@ -90,4 +88,42 @@ class User < ApplicationRecord lndhub.authenticate self lndhub.addinvoice payload end + + def dn + return @dn if defined?(@dn) + @dn = Devise::LDAP::Adapter.get_dn(self.cn) + end + + def ldap_entry + ldap.fetch_users(uid: self.cn, ou: self.ou).first + end + + def services_enabled + ldap_entry[:service] || [] + end + + def enable_service(service) + current_services = services_enabled + new_services = Array(service).map(&:to_s) + services = (current_services + new_services).uniq + ldap.replace_attribute(dn, :service, services) + end + + def disable_service(service) + current_services = services_enabled + disabled_services = Array(service).map(&:to_s) + services = (current_services - disabled_services).uniq + ldap.replace_attribute(dn, :service, services) + end + + def disable_all_services + ldap.delete_attribute(dn,:service) + end + + private + + def ldap + return @ldap_service if defined?(@ldap_service) + @ldap_service = LdapService.new + end end diff --git a/app/services/ldap_service.rb b/app/services/ldap_service.rb index baff4c9..5a572c5 100644 --- a/app/services/ldap_service.rb +++ b/app/services/ldap_service.rb @@ -3,6 +3,18 @@ class LdapService < ApplicationService @suffix = ENV["LDAP_SUFFIX"] || "dc=kosmos,dc=org" end + def add_attribute(dn, attr, values) + ldap_client.add_attribute dn, attr, values + end + + def replace_attribute(dn, attr, values) + ldap_client.replace_attribute dn, attr, values + end + + def delete_attribute(dn, attr) + ldap_client.delete_attribute dn, attr + end + def add_entry(dn, attrs, interactive=false) puts "Adding entry: #{dn}" if interactive res = ldap_client.add dn: dn, attributes: attrs @@ -10,10 +22,6 @@ class LdapService < ApplicationService res end - def add_attribute(dn, attr, value) - ldap_client.add_attribute dn, attr, value - end - def delete_entry(dn, interactive=false) puts "Deleting entry: #{dn}" if interactive res = ldap_client.delete dn: dn @@ -42,18 +50,17 @@ class LdapService < ApplicationService treebase = ldap_config["base"] end - attributes = %w{dn cn uid mail admin} + attributes = %w{dn cn uid mail admin service} filter = Net::LDAP::Filter.eq("uid", args[:uid] || "*") entries = ldap_client.search(base: treebase, filter: filter, attributes: attributes) entries.sort_by! { |e| e.cn[0] } - entries = entries.collect do |e| { uid: e.uid.first, mail: e.try(:mail) ? e.mail.first : nil, - admin: e.try(:admin) ? 'admin' : nil - # password: e.userpassword.first + admin: e.try(:admin) ? 'admin' : nil, + service: e.try(:service) } end end @@ -131,5 +138,4 @@ class LdapService < ApplicationService def ldap_config ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env] end - end diff --git a/app/views/admin/users/show.html.erb b/app/views/admin/users/show.html.erb index 126aed9..4679e6f 100644 --- a/app/views/admin/users/show.html.erb +++ b/app/views/admin/users/show.html.erb @@ -1,63 +1,97 @@ <%= render HeaderComponent.new(title: "User: #{@user.address}") %> <%= render MainSimpleComponent.new do %> +
+
+

Account

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Created at<%= @user.created_at.strftime("%Y-%m-%d (%H:%M UTC)") %>
Confirmed at + <% if @user.confirmed_at %> + <%= @user.confirmed_at.strftime("%Y-%m-%d (%H:%M UTC)") %> + <% else %> + <%= badge "pending", :yellow %> + <% end %> +
Email<%= @user.email %>
Roles<%= @user.is_admin? ? badge("admin", :red) : "—" %>
Invited by + <% if @user.inviter %> + <%= link_to @user.inviter.address, admin_user_path(@user.inviter.address), class: 'ks-text-link' %> + <% else %>—<% end %> +
Invitations available + <%= @user.invitations.count %> +
Invited users + <% if @user.invitees.length > 0 %> +
    + <% @user.invitees.order(cn: :asc).each do |invitee| %> +
  • <%= link_to invitee.address, admin_user_path(invitee.address), class: 'ks-text-link' %>
  • + <% end %> +
+ <% else %>—<% end %> +
+
+ +
+ +
+
+
-

Account

- +

Services

+
- - + + - - + + - - + + - - + + - - - - - - - - - - + +
Created at<%= @user.created_at.strftime("%Y-%m-%d (%H:%M UTC)") %>Discourse<%= check_box_tag 'service_discourse', 'enabled', @services_enabled.include?("discourse"), disabled: true %>
Confirmed at - <% if @user.confirmed_at %> - <%= @user.confirmed_at.strftime("%Y-%m-%d (%H:%M UTC)") %> - <% else %> - <%= badge "pending", :yellow %> - <% end %> - Gitea<%= check_box_tag 'service_gitea', 'enabled', @services_enabled.include?("gitea"), disabled: true %>
Email<%= @user.email %>Mastodon<%= check_box_tag 'service_mastodon', 'enabled', @services_enabled.include?("mastodon"), disabled: true %>
Roles<%= @user.is_admin? ? badge("admin", :red) : "—" %>Wiki<%= check_box_tag 'service_wiki', 'enabled', @services_enabled.include?("wiki"), disabled: true %>
Invited by - <% if @user.inviter %> - <%= link_to @user.inviter.address, admin_user_path(@user.inviter.address), class: 'ks-text-link' %> - <% else %>—<% end %> -
Invitations available - <%= @user.invitations.count %> -
Invited users - <% if @user.invitees.length > 0 %> -
    - <% @user.invitees.order(cn: :asc).each do |invitee| %> -
  • <%= link_to invitee.address, admin_user_path(invitee.address), class: 'ks-text-link' %>
  • - <% end %> -
- <% else %>—<% end %> -
XMPP<%= check_box_tag 'service_xmpp', 'enabled', @services_enabled.include?("xmpp"), disabled: true %>
- <% if Setting.lndhub_admin_enabled? %> + <% if Setting.lndhub_admin_enabled? && @user.confirmed? %>

LndHub

<% if @lndhub_user %> diff --git a/config/routes.rb b/config/routes.rb index ee16ee8..e137a92 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ require 'sidekiq/web' Rails.application.routes.draw do - devise_for :users + devise_for :users, :controllers => { :confirmations => "users/confirmations" } get 'welcome', to: 'welcome#index' get 'check_your_email', to: 'welcome#check_your_email' diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index ed135d9..b1724f1 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,7 +1,16 @@ require 'rails_helper' RSpec.describe User, type: :model do - let(:user) { create :user } + let(:user) { create :user, cn: "philipp" } + let(:dn) { "cn=philipp,ou=kosmos.org,cn=users,dc=kosmos,dc=org" } + + describe "#address" do + let(:user) { build :user, cn: "jimmy", ou: "kosmos.org" } + + it "returns the user address" do + expect(user.address).to eq("jimmy@kosmos.org") + end + end describe "#is_admin?" do it "returns true when admin flag is set in LDAP" do @@ -21,11 +30,75 @@ RSpec.describe User, type: :model do end end - describe "#address" do - let(:user) { build :user, cn: "jimmy", ou: "kosmos.org" } - - it "returns the user address" do - expect(user.address).to eq("jimmy@kosmos.org") + describe "#services_enabled" do + it "returns the entries from the LDAP service attribute" do + expect(user).to receive(:ldap_entry).and_return({ + uid: user.cn, ou: user.ou, mail: user.email, admin: nil, + service: ["discourse", "gitea", "wiki", "xmpp"] + }) + expect(user.services_enabled).to eq(["discourse", "gitea", "wiki", "xmpp"]) end end + + describe "#enable_service" do + before do + allow(user).to receive(:ldap_entry).and_return({ + uid: user.cn, ou: user.ou, mail: user.email, admin: nil, + service: ["discourse", "gitea"] + }) + allow(user).to receive(:dn).and_return(dn) + end + + it "adds the service to the LDAP entry" do + expect_any_instance_of(LdapService).to receive(:replace_attribute) + .with(dn, :service, ["discourse", "gitea", "wiki"]).and_return(true) + + user.enable_service(:wiki) + end + + it "adds multiple service to the LDAP entry" do + expect_any_instance_of(LdapService).to receive(:replace_attribute) + .with(dn, :service, ["discourse", "gitea", "wiki", "xmpp"]).and_return(true) + + user.enable_service([:wiki, :xmpp]) + end + end + + describe "#disable_service" do + before do + allow(user).to receive(:ldap_entry).and_return({ + uid: user.cn, ou: user.ou, mail: user.email, admin: nil, + service: ["discourse", "gitea", "xmpp"] + }) + allow(user).to receive(:dn).and_return(dn) + end + + it "removes the service from the LDAP entry" do + expect_any_instance_of(LdapService).to receive(:replace_attribute) + .with(dn, :service, ["discourse", "gitea"]).and_return(true) + + user.disable_service(:xmpp) + end + + it "removes multiple services from the LDAP entry" do + expect_any_instance_of(LdapService).to receive(:replace_attribute) + .with(dn, :service, ["discourse"]).and_return(true) + + user.disable_service([:xmpp, "gitea"]) + end + end + + describe "#disable_all_services" do + before do + allow(user).to receive(:dn).and_return(dn) + end + + it "removes all services from the LDAP entry" do + expect_any_instance_of(LdapService).to receive(:delete_attribute) + .with(dn, :service).and_return(true) + + user.disable_all_services + end + end + end